Пример #1
0
            /// <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);
            }
Пример #2
0
        /// <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);
        }
Пример #3
0
        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();
        }
Пример #4
0
 /// <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];
 }
Пример #5
0
        /// <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);
        }
Пример #6
0
 public Bank(MetroidRom rom, int index, bool isFixed)
 {
     this.Index  = index;
     this.Rom    = rom;
     this.Fixed  = isFixed;
     this.Offset = 0x4000 * index + 0x10;
 }
Пример #7
0
        /// <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);
        }
Пример #8
0
        /// <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;
            }
        }
Пример #9
0
        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);
        }
Пример #10
0
        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);
            }
        }
Пример #11
0
        public RomFormat(MetroidRom rom)
        {
            this.Rom    = rom;
            MapLocation = FormatMapLocation.Unspecified;
            Banks       = _Banks.AsReadOnly();

            LoadBanks();
        }
Пример #12
0
        /// <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;
        }
Пример #13
0
 /// <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;
 }
Пример #14
0
        /// <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();
        }
Пример #15
0
            /// <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();
            }
Пример #16
0
 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;
 }
Пример #17
0
        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;
            }
        }
Пример #18
0
 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);
 }
Пример #19
0
 internal ChrDumper(MetroidRom rom, LevelIndex level)
 {
     this.Rom     = rom;
     this.LevelId = level;
     if (level != LevelIndex.None)
     {
         LevelData = rom.Levels[level];
     }
     ChrBuffer = new byte[0x2000];
 }
Пример #20
0
        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, };
        }
Пример #21
0
        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.");
            }
        }
Пример #22
0
        public Project(MetroidRom rom)
            : this()
        {
            Files = _Files;
            this._expansionFile  = ProjectResources.DefaultMainFile;
            this.DefinesFile     = ProjectResources.DefaultDefinesFile;
            this.GeneralCodeFile = ProjectResources.DefaultGeneralFile;

            this.rom = rom;
        }
Пример #23
0
        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++;
        }
Пример #24
0
        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();
        }
Пример #25
0
        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;
        }
Пример #26
0
        /// <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;
        }
Пример #27
0
                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++;
                        }
                    }
                }
Пример #28
0
        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;
            }
        }
Пример #29
0
        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);
        }
Пример #30
0
        //-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);
        }