/// <summary> /// Returns a new ROM image containing the ROM resulting from the build process, or null if there are fatal errors. /// </summary> /// <returns></returns> public MetroidRom BuildRom() { if (_built) { throw new InvalidOperationException("Can not call BuildRom more than once."); } _built = true; sourceRom.SerializeAllData(); this.source = sourceRom.data; byte[] output = new byte[source.Length]; Array.Copy(source, output, source.Length); this.outputRom = new MetroidRom(new MemoryStream(output)); this.screenList = new ScreenList(outputRom); CopyScreenASM(); Assemble(); if (FatalError) { return(null); } return(outputRom); }
/// <summary> /// Creates a new EditorData for the specified ROM. If possible, EditorData /// should be loaded from the ROM using LoadEditorData instead. /// </summary> public static EditorData CreateNewForROM(MetroidRom rom) { EditorData data = new EditorData(rom); if (rom.RomFormat == RomFormats.Standard) { data.VersionMajor = 1; data.VersionMinor = 0; } else { data.VersionMajor = 2; if (rom.RomFormat == RomFormats.Expando) { data.VersionMinor = 0; } else if (rom.RomFormat == RomFormats.Enhanco) { // An enhanced ROM is 2.5, but the only difference // between 2.5 and 2.6 is the EditorData, so when this // EditorData is saved, the ROM will become 2.6. data.VersionMinor = 6; } else { // Versions beyond 2.6 aren't addressed here. In theory // anything beyond 2.6 will already have an EditorData // section. data.VersionMinor = 6; } } return(data); }
public Expander(MetroidRom rom) { if (rom == null || rom.data.Length >= ExpandoRomSize) { throw new ArgumentException("Specified rom image must be unexpanded"); } if (rom.data.Length < StandardRomSize) { throw new ArgumentException("Specified rom image too small"); } this.rom = rom; this.romData = rom.data; PerformSimpleMmcExpand(); foreach (var level in levels) { MoveBgPatterns(level); MoveSpritePatterns(level); } CopyMiscPatterns(); DeleteOldPatterns(); ApplyAsmPatches(); RelocateData(); }
/// <summary>Creates and initializes an AlternateMusicRooms object.</summary> /// <param name="rom">The rom that contains the data.</param> /// <param name="level">The level this object represents.</param> public AlternateMusicRooms(MetroidRom rom, Level level) { this.rom = rom; ////this.level = level; this.Level = level; this.offset = level.Format.AltMusicOffset; // offsets[(int)Level.Index]; }
/// <summary> /// Loads animation data/frames for the specified level. /// </summary> /// <param name="level">The level to load data for</param> /// <param name="lastFrame">The index of the first frame after the current area's frame data, or FF to leave unspecified. If an FF terminator is encountered in frame data, parsing will stop sooner.</param> /// <returns></returns> private static ChrAnimationLevelData LoadAnimationsForLevel(MetroidRom rom, LevelIndex level, int lastFrame) { ChrAnimationLevelData data = new ChrAnimationLevelData(level); // CHR usage table has been repurposed in MMC3 ROMs (hence accessor names don't match usage) data.SprBank0 = rom.ChrUsage.GetSprPage(level); data.SprBank1 = rom.ChrUsage.GetBgLastPage(level); // First frame index int frameIndex = rom.ChrUsage.GetBgFirstPage(level); bool keepGoing = true; while (keepGoing && frameIndex < lastFrame && frameIndex < 0xFF) { ChrAnimationTable loadedAnimation = LoadOneAnimation(rom, level, ref frameIndex); if (loadedAnimation == null) { if (data.Animations.Count == 0) { loadedAnimation = new ChrAnimationTable(); ChrAnimationFrame frame = new ChrAnimationFrame(); frame.FrameTime = 1; loadedAnimation.Frames.Add(frame); } else { return(data); } } data.Animations.Add(loadedAnimation); } return(data); }
public Bank(MetroidRom rom, int index, bool isFixed) { this.Index = index; this.Rom = rom; this.Fixed = isFixed; this.Offset = 0x4000 * index + 0x10; }
/// <summary> /// Returns an EditorData object loaded from the ROM, or null if the ROM does not contain an EditorData section. /// If the ROM does not contain an EditorData section, a new EditorData object can be created via the /// CreateNewForROM function. /// </summary> public static EditorData LoadEditorData(MetroidRom rom) { if (rom.Banks.Count < 0xe) { return(null); } var dataBank = rom.Banks[0xe]; pCpu pData = RomFormat.EditorDataPointer; for (int i = 0; i < MagicNumber.Length; i++) { if (dataBank[pData] != MagicNumber[i]) { return(null); } pData += 1; } EditorData result = new EditorData(rom); using (var r = new BinaryReader(new MemoryStream(rom.data, dataBank.ToOffset(pData), 0x4000))) { result.VersionMajor = r.ReadByte(); result.VersionMinor = r.ReadByte(); result.HasAssociatedProject = r.ReadBoolean(); } return(result); }
/// <summary> /// Loads level-specific pattern groups. /// </summary> /// <param name="level">The level to load patter groups for.</param> public PatternGroupIndexTable(Level level) { this.level = level; this.rom = level.Rom; this.ranges = level.Rom.PatternGroupOffsets; if (level.Index == LevelIndex.Brinstar) { indexOffsets = PatternGroupIndexOffsets.Brinstar; } else if (level.Index == LevelIndex.Norfair) { indexOffsets = PatternGroupIndexOffsets.Norfair; } else if (level.Index == LevelIndex.Tourian) { indexOffsets = PatternGroupIndexOffsets.Tourian; } else if (level.Index == LevelIndex.Kraid) { indexOffsets = PatternGroupIndexOffsets.Kraid; } else if (level.Index == LevelIndex.Ridley) { indexOffsets = PatternGroupIndexOffsets.Ridley; } }
private static ChrAnimationTable LoadOneAnimation(MetroidRom rom, LevelIndex level, ref int frameIndex) { ChrAnimationTable result = new ChrAnimationTable(); if (isTerminator(rom, frameIndex)) { return(null); } bool lastFrame = false; while (!lastFrame) { ChrAnimationFrame?loadedFrame = LoadOneFrame(rom, frameIndex, out lastFrame); // If we fail to load a frame, that means there is an erroneous condition and this animation is invalid. if (loadedFrame == null) { return(null); } result.Frames.Add(loadedFrame.Value); frameIndex++; } return(result); }
public Project(MetroidRom rom, Stream projFile) : this() { Files = _Files; this.rom = rom; for (int i = 0; i < magicNumber.Length; i++) { if (projFile.ReadByte() != magicNumber[i]) { throw new ProjectLoadException("Project file is missing magic number"); } } BinaryReader reader = new BinaryReader(projFile); //_expansionFile = reader.ReadString(); LoadEntries(reader, false); if (ExtendedFormat) { LoadEntries(reader, true); } }
public RomFormat(MetroidRom rom) { this.Rom = rom; MapLocation = FormatMapLocation.Unspecified; Banks = _Banks.AsReadOnly(); LoadBanks(); }
/// <summary> /// Loads global pattern groups. /// </summary> /// <param name="rom">The ROM to load pattern groups for.</param> public PatternGroupIndexTable(MetroidRom rom) { this.level = null; this.rom = rom; this.ranges = rom.PatternGroupOffsets; indexOffsets = PatternGroupIndexOffsets.Global; }
/// <summary> /// Constructor. /// </summary> /// <param name="currentLevelIndex"></param> /// <param name="data"></param> /// <param name="offset"></param> public ItemRowEntry(LevelIndex level, MetroidRom rom, int offset, int index) { this.index = index; this.offset = offset; this.data = rom.data; this.level = level; this.rom = rom; }
/// <summary> /// Constructor. /// </summary> /// <param name="currentLevelIndex"></param> /// <param name="data"></param> public ItemLoader(Level level, MetroidRom rom) { this.Level = level; this.data = rom.data; this.rom = rom; LoadEntries(); }
/// <summary> /// /// </summary> /// <param name="rom"></param> /// <param name="project"></param> /// <param name="outputFileName">Not used directly. Used for the generation of additional files whose names must be related (e.g. debug files). Should be file name only (i.e. NOT full path)</param> public Builder(MetroidRom rom, Project project, string outputFileName) { this.sourceRom = rom; this.project = project; this.OutputFilename = outputFileName; this.Errors = this._Errors.AsReadOnly(); }
public Mmc3RomFormat(MetroidRom rom) : base(rom) { // "Enhanced memory management" (adding/removing level data items, e.g. screens/items/etc, may be removed from all but MMC3 ROMs for simplicity) SupportsEnhancedMemoryManagement = true; HasPrgAllocationTable = true; SupportsCustomTilePhysics = true; }
private void WriteDefaultTilePhysics(MetroidRom r, LevelIndex level) { var tilePhysicsOffset = (int)r.Levels[level].TilePhysicsTableLocation; for (int i = 0; i < 256; i++) { r.data[tilePhysicsOffset + i] = (byte)i; } }
private static bool isTerminator(MetroidRom rom, int frameIndex) { return (rom.GetAnimationTable_Bank0(frameIndex) == 0xFF && rom.GetAnimationTable_Bank1(frameIndex) == 0xFF && rom.GetAnimationTable_Bank2(frameIndex) == 0xFF && rom.GetAnimationTable_Bank3(frameIndex) == 0xFF && rom.GetAnimationTable_FrameData(frameIndex) == 0xFF); }
internal ChrDumper(MetroidRom rom, LevelIndex level) { this.Rom = rom; this.LevelId = level; if (level != LevelIndex.None) { LevelData = rom.Levels[level]; } ChrBuffer = new byte[0x2000]; }
public LevelCollection(MetroidRom rom) { brinstar = new Level(rom, LevelIndex.Brinstar); ridley = new Level(rom, LevelIndex.Ridley); norfair = new Level(rom, LevelIndex.Norfair); kraid = new Level(rom, LevelIndex.Kraid); tourian = new Level(rom, LevelIndex.Tourian); values = new Level[] { brinstar, norfair, tourian, kraid, ridley, }; }
public Enhancer(MetroidRom rom) { this.Rom = rom; this.romData = rom.data; if (rom.data.Length != ExpandoSize) { throw new ArgumentException("The specified ROM is not a proper Expando ROM (the size is not correct."); } }
public Project(MetroidRom rom) : this() { Files = _Files; this._expansionFile = ProjectResources.DefaultMainFile; this.DefinesFile = ProjectResources.DefaultDefinesFile; this.GeneralCodeFile = ProjectResources.DefaultGeneralFile; this.rom = rom; }
internal static void SerializeChrAnimationTerminator(MetroidRom rom, ref int frameIndex) { rom.SetAnimationTable_Bank0(frameIndex, 0xFF); rom.SetAnimationTable_Bank1(frameIndex, 0xFF); rom.SetAnimationTable_Bank2(frameIndex, 0xFF); rom.SetAnimationTable_Bank3(frameIndex, 0xFF); rom.SetAnimationTable_FrameTime(frameIndex, 0xFF); rom.SetAnimationTable_FrameLast(frameIndex, true); frameIndex++; }
public MMC3Expander(MetroidRom rom, Project p) { this.originalRom = rom; this.project = p; oldRomImage = new byte[rom.data.Length]; //newRomImage = new byte[oldRomImage.Length]; Array.Copy(rom.data, oldRomImage, rom.data.Length); Array.Copy(oldRomImage, newRomImage, rom.data.Length); //ExpandRom(); }
public PatternGroupOffsetTable(MetroidRom rom) { this.IsReadOnly = false; this.rom = rom; for (int i = 0; i < entryCount; i++) { Add(new PatternGroupOffsets(rom, i)); } this.IsReadOnly = true; }
/// <summary> /// Converts a CHR Usage table (enhanco) to a CHR Animation table (MMC3). The ROM must have a valid CHR Usage table with a total of less than 256 frames for all levels together. /// </summary> /// <param name="r"></param> /// <param name="p"></param> public static void ConvertChrAnimation(MetroidRom r, Project p) { int animationIndex = 0; CreateChrAnimationTablesForLevel(r, p, LevelIndex.None, ref animationIndex); CreateChrAnimationTablesForLevel(r, p, LevelIndex.Brinstar, ref animationIndex); CreateChrAnimationTablesForLevel(r, p, LevelIndex.Norfair, ref animationIndex); CreateChrAnimationTablesForLevel(r, p, LevelIndex.Tourian, ref animationIndex); CreateChrAnimationTablesForLevel(r, p, LevelIndex.Kraid, ref animationIndex); CreateChrAnimationTablesForLevel(r, p, LevelIndex.Ridley, ref animationIndex); // If the ROM has this flag set, it needs to be cleared so the CHR animation table is properly serialized r.ChrAnimationTableMissing = false; }
public ScreenList(MetroidRom rom) { this.rom = rom; _screens = new List <ScreenListItem> [LevelCount]; int id = ScreenPatchID_0; for (int i = 0; i < LevelCount; i++) { _screens[i] = new List <ScreenListItem>(); var level = rom.Levels[(LevelIndex)i]; for (int iScreen = 0; iScreen < level.Screens.Count; iScreen++) { _screens[i].Add(new ScreenListItem(id, level.Screens[iScreen])); id++; } } }
private static void AddAnimationFrames(MetroidRom rom, ref int frameIndex, int firstChrBank, int frameCount, int frameLength) { var chrBank = firstChrBank; while (frameCount > 0) { rom.SetAnimationTable_Bank0(frameIndex, (byte)chrBank); rom.SetAnimationTable_Bank1(frameIndex, (byte)(chrBank + 1)); rom.SetAnimationTable_Bank2(frameIndex, (byte)(chrBank + 2)); rom.SetAnimationTable_Bank3(frameIndex, (byte)(chrBank + 3)); rom.SetAnimationTable_FrameTime(frameIndex, frameLength); // Set the 'last frame' flag on the last frame rom.SetAnimationTable_FrameLast(frameIndex, frameCount == 1); frameCount--; frameIndex++; chrBank += 4; } }
private static ChrAnimationFrame?LoadOneFrame(MetroidRom rom, int frameIndex, out bool LastFrame) { LastFrame = false; if (frameIndex == 0xFF) { return(null); } ChrAnimationFrame result = new ChrAnimationFrame(); result.Bank0 = rom.GetAnimationTable_Bank0(frameIndex); //rom.data[TableOffset_Bank0 + frameIndex]; result.Bank1 = rom.GetAnimationTable_Bank1(frameIndex); //rom.data[TableOffset_Bank0 + frameIndex]; result.Bank2 = rom.GetAnimationTable_Bank2(frameIndex); //rom.data[TableOffset_Bank0 + frameIndex]; result.Bank3 = rom.GetAnimationTable_Bank3(frameIndex); //rom.data[TableOffset_Bank0 + frameIndex]; // Don't store last-frame bit in result.Frame! result.FrameTime = rom.GetAnimationTable_FrameTime(frameIndex); //0x7F & rom.data[TableOffset_Bank0 + frameIndex]; LastFrame = rom.GetAnimationTable_FrameLast(frameIndex); //0 != (rom.data[TableOffset_Bank0 + frameIndex] & 0x80); return(result); }
//-1E:B600 (0x7B610) = CHR animation table //-B600: Slot 0 bank index //-B700: Slot 1 bank index //-B800: Slot 2 bank index //-B900: Slot 3 bank index //-BA00: Lower 7 bits: frame delay / High bit: if set, last frame ////public const int TableOffset_Bank0 = 0x7B610; ////public const int TableOffset_Bank1 = 0x7B710; ////public const int TableOffset_Bank2 = 0x7B810; ////public const int TableOffset_Bank3 = 0x7B910; ////public const int TableOffset_FrameData = 0x7BA10; public static ChrAnimationRomData DeserializeChrAnimation(MetroidRom r, Project p) { ChrAnimationRomData result = new ChrAnimationRomData(); var titleStart = r.ChrUsage.GetBgFirstPage(LevelIndex.None); var brinStart = r.ChrUsage.GetBgFirstPage(LevelIndex.Brinstar); var norStart = r.ChrUsage.GetBgFirstPage(LevelIndex.Norfair); var tourStart = r.ChrUsage.GetBgFirstPage(LevelIndex.Tourian); var kraidStart = r.ChrUsage.GetBgFirstPage(LevelIndex.Kraid); var ridStart = r.ChrUsage.GetBgFirstPage(LevelIndex.Ridley); result.Add(LevelIndex.None, LoadAnimationsForLevel(r, LevelIndex.None, brinStart)); result.Add(LevelIndex.Brinstar, LoadAnimationsForLevel(r, LevelIndex.Brinstar, norStart)); result.Add(LevelIndex.Norfair, LoadAnimationsForLevel(r, LevelIndex.Norfair, tourStart)); result.Add(LevelIndex.Tourian, LoadAnimationsForLevel(r, LevelIndex.Tourian, kraidStart)); result.Add(LevelIndex.Kraid, LoadAnimationsForLevel(r, LevelIndex.Kraid, ridStart)); result.Add(LevelIndex.Ridley, LoadAnimationsForLevel(r, LevelIndex.Ridley, 0xFF)); return(result); }