public static void SaveBlocksToRom(Rom rom, int course) { //grab all levels data at once byte[][] allTiles = new byte[0x2B][]; for (int i = 0; i < 0x2B; i++) { rom.SetBank(0xC); int headerposition = rom.ReadWord(0x4560 + i * 2); int tilebank = rom.ReadByte(headerposition + 9); int tilesubbank = rom.ReadByte(headerposition + 10); rom.SetBank(tilebank); int tilePosition = rom.ReadWord(0x4000 + tilesubbank * 2); byte[] tileData = RLEDecompressTiles(rom, tilePosition); allTiles[i] = tileData; } Array.Copy(levelData, allTiles[course], 0x3000); //write them back to ROM byte bank = 0x20; byte subbank = 0; int writePosition = 0x4040; //first 64 bytes are reserved for level pointers for (int i = 0; i < 0x2B; i++) { byte[] tileData = RLECompressTiles(allTiles[i]).ToArray(); if ((writePosition + tileData.Length) >= 0x8000 || subbank >= 32) { //no more space, switch to another bank bank++; subbank = 0; writePosition = 0x4040; } //write data to bank rom.SetBank(bank); rom.WriteWord(0x4000 + subbank * 2, (ushort)writePosition); rom.WriteBytes(writePosition, tileData); //update level header rom.SetBank(0xC); int headerposition = rom.ReadWord(0x4560 + i * 2); rom.WriteByte(headerposition + 9, bank); rom.WriteByte(headerposition + 10, subbank); subbank++; writePosition += tileData.Length; } }
public static bool SaveObjectsToRom(Rom rom, int course, out string errorMessage) { byte[][] enemyData = new byte[0x2B][]; //save objects rom.SetBank(0x7); for (int i = 0; i < 0x2B; i++) { int objectsPos = rom.ReadWord(0x4199 + i * 2); enemyData[i] = RLEDecompressObjects(rom, objectsPos); } enemyData[course] = objectsData; int objectSize = enemyData.Sum(x => RLECompressObjects(x).Count()); if (objectSize > 4198) { errorMessage = string.Format("Object data is too big to fit in ROM.\r\nPlease remove some objects to free at least {0} byte(s).", objectSize - 4198); return(false); } int startPos = rom.ReadWord(0x4199); for (int i = 0; i < 0x2B; i++) { rom.WriteWord(0x4199 + i * 2, (ushort)startPos); byte[] data = RLECompressObjects(enemyData[i]).ToArray(); rom.WriteBytes(startPos, data); startPos += data.Length; } errorMessage = string.Empty; return(true); }
static void DumpEnemiesSprites(Rom rom, int enemiesIdsPointer, int tilesDataAddress) { loadedSprites = new Rectangle[6]; enemiesAvailable = new bool[6]; Array.Clear(tilesEnemies.Bits, 0, tilesEnemies.Bits.Length); for (int i = 0; i < 6; i++) { rom.SetBank(0x7); int enemyId = rom.ReadWord(enemiesIdsPointer + (i + 1) * 2); if (enemyId != 0x530F) { enemiesAvailable[i] = true; int spriteDataAddress; if (enemiesIdsToSpriteData.TryGetValue(enemyId, out spriteDataAddress)) { if (LoadEnemiesTiles(rom, i, spriteDataAddress, tilesDataAddress)) { Rectangle rectangle = DumpSprite(rom, 32, 56 + i * 64, spriteDataAddress, tilesEnemies); loadedSprites[i] = rectangle; } } } } }
static bool LoadEnemiesTiles(Rom rom, int enemyIndex, int spriteDataAddress, int tilesDataAddress) { int index = 0; do { rom.SetBank(0x7); int tilesBank = rom.ReadByte(tilesDataAddress++); int tilesAddress = rom.ReadWord(tilesDataAddress++); tilesDataAddress++; int tilesCount = rom.ReadByte(tilesDataAddress++); if (tilesCount == 0xFF) { return(false); } if (index == enemyIndex) { LoadEnemiesTilesInternal(rom, spriteDataAddress, tilesBank, tilesCount, tilesAddress); return(true); } index++; }while(true); }
public static void DumpBonusSprites(Rom rom) { Array.Clear(tiles8x8.Bits, 0, tiles8x8.Bits.Length); //bonus sprites rom.SetBank(0x5); Dump8x8Tiles(rom, 0x4C81, 23, 0, 0x1E, enemyPalette, true); rom.SetBank(0x11); Dump8x8Tiles(rom, 0x7300, 4, 16 + 23, 0x1E, enemyPalette, true); rom.SetBank(0xF); int[] sprites = { 0x4ACB, 0x4AEB, 0x4ADB, 0x4AFB, 0x4B0B, 0x4B1B, 0x4B2B, 0x4B4B, 0x4B5B }; for (int i = 0; i < sprites.Length; i++) { DumpSprite(rom, 8 + i * 16, 16, sprites[i], tilesObjects); } }
static void LoadEnemiesTilesInternal(Rom rom, int spriteDataAddress, int tilesBank, int tilesCount, int tilesAddress) { rom.SetBank(tilesBank); int pos = rom.ReadWord(spriteDataAddress); int spriteFlags = rom.ReadByte(pos); int palette = (spriteFlags & 0x10) != 0 ? 0xD1 : 0x1E; Array.Clear(tiles8x8.Bits, 0, tiles8x8.Bits.Length); Dump8x8Tiles(rom, tilesAddress, tilesCount, 0, (byte)palette, enemyPalette, true); }
public static void DumpPlayerSprite(Rom rom) { Array.Clear(tiles8x8.Bits, 0, tiles8x8.Bits.Length); rom.SetBank(0x5); Dump8x8Tiles(rom, 0x42F1, 64, 0, 0x1E, enemyPalette, true); playerRectangles = new [] { DumpSpritePlayer(rom, 32, 56, 0x603B, false), DumpSpritePlayer(rom, 32, 56 + 64, 0x603B, true) }; }
public static List <KeyValuePair <int, int> > GetCourseIds(Rom rom) { //convert course id => course no using data in ROM rom.SetBank(0); var courseIdToNo = new List <KeyValuePair <int, int> >(); for (int i = 0; i <= 0x2A; i++) { int levelpointer = rom.ReadWord(0x0534 + i * 2); int courseNo = (levelpointer - 0x0587) / 3; courseIdToNo.Add(new KeyValuePair <int, int>(i, courseNo)); } return(courseIdToNo); }
public static int SearchWarp(Rom rom, int course, int sector) { rom.SetBank(0xC); //search sector that target that sector for (int i = 0; i < 32; i++) { int warp = rom.ReadWord(0x4F30 + course * 64 + i * 2); if (rom.ReadByte(warp) == sector) { return(warp); } } return(-1); }
static void FindEnemiesData(Rom rom, int enemiesPointer, out int enemyId, out int tilesPointer) { byte[] pattern = { 0x3E, 0x00, //ld a, .. 0xEA, 0x2F, 0xA3, //ld (A32F),a 0x3E, 0x00, //ld a, .. 0xEA, 0x30, 0xA3, //ld (A330),a 0x01, 0x00, 0x00, //ld bc, .. 0xCD, 0xA7, 0x40 //call 40A7 }; rom.SetBank(0x7); int position = enemiesPointer; while (rom.ReadByte(position) != 0xC9) //ret { bool match = true; for (int j = 0; j < pattern.Length; j++) { byte valueFromRom = rom.ReadByte(position + j); byte valueToCompare = pattern[j]; if (valueToCompare != 0x00 && valueFromRom != valueToCompare) { match = false; break; } } if (match) { enemyId = rom.ReadByte(position + 6) << 8 | rom.ReadByte(position + 1); tilesPointer = rom.ReadByte(position + 12) << 8 | rom.ReadByte(position + 11); return; } position++; } throw new Exception("cannot find enemies data"); }
public static void DumpLevel(Rom rom, int course, int warp, Bitmap tiles8x8, Bitmap tiles16x16, bool reloadAll, bool switchA, bool switchB, int paletteIndex) { rom.SetBank(0xC); int header = rom.ReadWord(0x4560 + course * 2); int tilebank = rom.ReadByte(header + 0); int tileaddressB = rom.ReadWord(header + 1); int tileaddressA = rom.ReadWord(header + 3); int tileanimated = rom.ReadWord(header + 7); int blockbank = rom.ReadByte(header + 9); int blockubbank = rom.ReadByte(header + 10); int blockindex = rom.ReadWord(header + 11); byte palette = rom.ReadByte(header + 27); //load warp if(warp != -1) { tilebank = rom.ReadByte(warp + 11); tileaddressB = rom.ReadWord(warp + 12); tileaddressA = rom.ReadWord(warp + 14); tileanimated = rom.ReadWord(warp + 18); blockindex = rom.ReadWord(warp + 20); palette = rom.ReadByte(warp + 10); } //set palette paletteColors = palettes[paletteIndex]; //dump 8x8 blocks using(Graphics g = Graphics.FromImage(tiles8x8)) { g.Clear(paletteColors[0]); rom.SetBank(0x11); Dump8x8Tiles(rom, tileaddressA, tiles8x8, 2*16, 0, palette); rom.SetBank(tilebank); Dump8x8Tiles(rom, tileaddressB, tiles8x8, 6*16, 2, palette); rom.SetBank(0x11); Dump8x8Tiles(rom, tileanimated, tiles8x8, 4, 2, palette); } //dump 16x16 blocks using(Graphics g = Graphics.FromImage(tiles16x16)) { g.Clear(paletteColors[0]); rom.SetBank(0xB); Dump16x16Tiles(rom, blockindex, tiles8x8, g, switchA, switchB); } if(reloadAll) { //dump level rom.SetBank(blockbank); int tilesdata = rom.ReadWord(0x4000 + blockubbank * 2); levelData = RLEDecompressTiles(rom, tilesdata).Take(0x3000).ToArray(); //dump scroll rom.SetBank(0xC); scrollData = rom.ReadBytes(0x4000 + course * 32, 32); //dump objects rom.SetBank(0x7); int objectsPosition = rom.ReadWord(0x4199 + course * 2); objectsData = RLEDecompressObjects(rom, objectsPosition).Take(0x2000).ToArray(); //dump warps rom.SetBank(0xC); warps = new Byte[32]; for(int i = 0 ; i < 32 ; i++) { int warpdata = rom.ReadWord(0x4F30 + course * 64 + i * 2); warps[i] = rom.ReadByte(warpdata); } warioPosition = rom.ReadWordSwap(header + 15) + rom.ReadWordSwap(header + 13) * 8192; } }
public static int SearchWarp(Rom rom, int course, int sector) { rom.SetBank(0xC); int warp = rom.ReadWord(0x4F30 + course * 64 + sector * 2); if(rom.ReadByte(warp) != 32) { //search sector that target that sector for(int i = 0 ; i < 32 ; i++) { warp = rom.ReadWord(0x4F30 + course * 64 + i * 2); if(rom.ReadByte(warp) == sector) { return warp; } } } return -1; }
public static bool SaveObjectsToRom(Rom rom, int course, out string errorMessage) { byte[][] enemyData = new byte[0x2B][]; //save objects rom.SetBank(0x7); for(int i = 0 ; i < 0x2B ; i++) { int objectsPos = rom.ReadWord(0x4199 + i * 2); enemyData[i] = RLEDecompressObjects(rom, objectsPos).Take(0x2000).ToArray(); } enemyData[course]= objectsData; int objectSize = enemyData.Sum(x => RLECompressObjects(x).Count()); if(objectSize > 4198) { errorMessage = string.Format("Object data is too big to fit in ROM.\r\nPlease remove at least {0} byte(s).", objectSize - 4198); return false; } int startPos = rom.ReadWord(0x4199); for(int i = 0 ; i < 0x2B ; i++) { rom.WriteWord(0x4199 + i * 2, (ushort)startPos); byte[] data = RLECompressObjects(enemyData[i]).ToArray(); rom.WriteBytes(startPos, data); startPos += data.Length; } errorMessage = string.Empty; return true; }
public static void SaveBlocksToRom(Rom rom, int course) { //grab all levels data at once byte[][] allTiles = new byte[0x2B][]; for(int i = 0 ; i < 0x2B ; i++) { rom.SetBank(0xC); int headerposition = rom.ReadWord(0x4560 + i * 2); int tilebank = rom.ReadByte(headerposition + 9); int tilesubbank = rom.ReadByte(headerposition + 10); rom.SetBank(tilebank); int tilePosition = rom.ReadWord(0x4000 + tilesubbank * 2); byte[] tileData = RLEDecompressTiles(rom, tilePosition).Take(0x3000).ToArray(); allTiles[i] = tileData; } Array.Copy(levelData, allTiles[course], 0x3000); //write them back to ROM byte bank = 0x20; byte subbank = 0; int writePosition = 0x4040; //first 64 bytes are reserved for level pointers for(int i = 0 ; i < 0x2B ; i++) { byte[] tileData = RLECompressTiles(allTiles[i]).ToArray(); if((writePosition + tileData.Length) >= 0x8000 || subbank >= 32) { //no more space, switch to another bank bank++; subbank = 0; writePosition = 0x4040; } //write data to bank rom.SetBank(bank); rom.WriteWord(0x4000 + subbank * 2, (ushort)writePosition); rom.WriteBytes(writePosition, tileData); //update level header rom.SetBank(0xC); int headerposition = rom.ReadWord(0x4560 + i * 2); rom.WriteByte(headerposition + 9, bank); rom.WriteByte(headerposition + 10, subbank); subbank++; writePosition += tileData.Length; } }
public static void DumpLevel(Rom rom, int course, int warp, bool reloadAll, int switchMode, int paletteIndex, int animatedTileIndex, bool reloadAnimatedTilesOnly) { int tilebank; int tileaddressB; int tileaddressA; int tileanimated; int blockindex; byte palette; int enemiesData; rom.SetBank(0xC); int header = rom.ReadWord(0x4560 + course * 2); if (warp != -1) { //load warp tilebank = rom.ReadByte(warp + 11); tileaddressB = rom.ReadWord(warp + 12); tileaddressA = rom.ReadWord(warp + 14); tileanimated = rom.ReadWord(warp + 18); blockindex = rom.ReadWord(warp + 20); animatedTilesMask = rom.ReadByte(warp + 9); palette = rom.ReadByte(warp + 10); enemiesData = rom.ReadWord(warp + 22); } else { //or header tilebank = rom.ReadByte(header + 0); tileaddressB = rom.ReadWord(header + 1); tileaddressA = rom.ReadWord(header + 3); tileanimated = rom.ReadWord(header + 7); blockindex = rom.ReadWord(header + 11); animatedTilesMask = rom.ReadByte(header + 26); palette = rom.ReadByte(header + 27); enemiesData = rom.ReadWord(header + 28); } Color[] paletteColors = palettes[paletteIndex]; if (!reloadAnimatedTilesOnly) { int enemiesIdsPointer, enemiesTiles; FindEnemiesData(rom, enemiesData, out enemiesIdsPointer, out enemiesTiles); DumpEnemiesSprites(rom, enemiesIdsPointer, enemiesTiles); //dump 8x8 tiles Array.Clear(tiles8x8.Bits, 0, tiles8x8.Width * tiles8x8.Height); rom.SetBank(0x11); Dump8x8Tiles(rom, tileaddressA, 2 * 16, 0 * 16, palette, paletteColors); rom.SetBank(tilebank); Dump8x8Tiles(rom, tileaddressB, 6 * 16, 2 * 16, palette, paletteColors); } if (animatedTilesMask != 0) { rom.SetBank(0x11); Dump8x8Tiles(rom, tileanimated + animatedTileIndex * 16 * 4, 4, 2 * 16, palette, paletteColors); } //dump 16x16 tiles if (reloadAnimatedTilesOnly) { DumpAnimated16x16Tiles(paletteColors[0]); } else { rom.SetBank(0xB); Dump16x16Tiles(rom, blockindex, switchMode, paletteColors[0], out animated16x16Tiles, out animated8x8Tiles); } if (reloadAll) { //dump 16x16 blocks rom.SetBank(0xC); int blockbank = rom.ReadByte(header + 9); int blockubbank = rom.ReadByte(header + 10); rom.SetBank(blockbank); int tilesdata = rom.ReadWord(0x4000 + blockubbank * 2); levelData = RLEDecompressTiles(rom, tilesdata); //dump scroll rom.SetBank(0xC); scrollData = rom.ReadBytes(0x4000 + course * 32, 32); //dump objects rom.SetBank(0x7); int objectsPosition = rom.ReadWord(0x4199 + course * 2); objectsData = RLEDecompressObjects(rom, objectsPosition); //dump warps rom.SetBank(0xC); warps = new Byte[32]; for (int i = 0; i < 32; i++) { int warpdata = rom.ReadWord(0x4F30 + course * 64 + i * 2); warps[i] = rom.ReadByte(warpdata); } warioPosition = rom.ReadWordSwap(header + 15) + rom.ReadWordSwap(header + 13) * 8192; warioRightFacing = (rom.ReadByte(header + 18) & 0x20) == 0; } }