static void Dump8x8Tiles(Rom rom, int gfxaddress, int tiles, int pos, byte palette, Color[] customPalette, bool transparency = false) { for (int n = 0; n < tiles; n++) { int tilePosX = ((n + pos) % 16) * 8; int tilePosY = ((n + pos) / 16) * 8; for (int y = 0; y < 8; y++) { byte data0 = rom.ReadByte(gfxaddress++); byte data1 = rom.ReadByte(gfxaddress++); int destIndex = tilePosX + (y + tilePosY) * tiles8x8.Width; for (int x = 0; x < 8; x++) { int pixelA = (data0 >> (7 - x)) & 0x1; int pixelB = (data1 >> (7 - x)) & 0x1; int pixel = pixelA + pixelB * 2; if (!transparency || pixel != 0) { int palindex = (palette >> pixel * 2) & 0x3; tiles8x8.Bits[destIndex + x] = customPalette[palindex].ToArgb(); } } } } }
static byte[] RLEDecompressTiles(Rom rom, int tilesdata) { byte[] decompressed = new byte[0x3000]; int position = 0; while (position < decompressed.Length) { byte data = rom.ReadByte(tilesdata++); if ((data & 0x80) == 0x80) { data = (byte)(data & 0x7F); byte rep = rom.ReadByte(tilesdata++); for (int j = 0; j <= rep; j++) { decompressed[position++] = data; } } else { decompressed[position++] = data; } } return(decompressed); }
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 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; } }
static void Dump16x16Tiles(Rom rom, int tileindexaddress, int switchMode, Color defaultColor, out bool[] anim16x16Tiles, out int[] anim8x8Tiles) { anim16x16Tiles = new bool[16 * 8]; anim8x8Tiles = new int[16 * 8 * 2 * 2]; int m = 0; for (int n = 0; n < 16; n++) { for (int i = 0; i < 8; i++) { int tileIndex = i + n * 8; int newTileIndex = SwitchTile(tileIndex, switchMode); bool isAnimatedTile = false; for (int k = 0; k < 2; k++) { for (int j = 0; j < 2; j++) { byte subTileIndex = rom.ReadByte(tileindexaddress + newTileIndex * 4 + k * 2 + j); bool isAnimated = subTileIndex >= (2 * 16) && subTileIndex < (2 * 16 + 4); isAnimatedTile |= isAnimated; animated8x8Tiles[m++] = isAnimated ? subTileIndex : -1; Point dest = new Point(j * 8 + i * 16, k * 8 + n * 16); Dump8x8Tile(dest, subTileIndex, defaultColor); } } anim16x16Tiles[tileIndex] = isAnimatedTile; } } }
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); }
static byte[] RLEDecompressObjects(Rom rom, int enemiesData) { int position = 0; byte[] decompressed = new byte[0x2000]; while (position < decompressed.Length) { byte data = rom.ReadByte(enemiesData++); decompressed[position++] = (byte)(data >> 4); decompressed[position++] = (byte)(data & 0xF); byte repeat = rom.ReadByte(enemiesData++); for (int i = 0; i < repeat; i++) { decompressed[position++] = 0; decompressed[position++] = 0; } } return(decompressed); }
public static void Dump8x8Tiles(Rom rom, int gfxaddress, Bitmap gfx8, int tiles, int rowpos, byte palette) { for(int n = 0 ; n < tiles ; n++) { for(int j = 0 ; j < 8 ; j++) { byte data0 = rom.ReadByte(gfxaddress++); byte data1 = rom.ReadByte(gfxaddress++); for(int i = 0 ; i < 8 ; i++) { int pixelA = (data0 >> (7 - i)) & 0x1; int pixelB = (data1 >> (7 - i)) & 0x1; int pixel = pixelA + pixelB * 2; int palindex = (palette >> pixel * 2) & 0x3; int x = i + n%16 * 8; int y = j + (n/16 + rowpos) * 8; gfx8.SetPixel(x, y, paletteColors[palindex]); } } } }
static Rectangle DumpSpritePlayer(Rom rom, int posx, int posy, int spriteAddress, bool horizontalFlip) { Rectangle rectangle = Rectangle.Empty; int pos = spriteAddress; Func <sbyte, sbyte> hflip = x => (sbyte)(horizontalFlip ? ((~(sbyte)x) - 7) : x); pos = rom.ReadWord(pos); while (rom.ReadByte(pos) != 0x80) { int spx, spy; unchecked { spy = (sbyte)rom.ReadByte(pos++) + posy; spx = hflip((sbyte)rom.ReadByte(pos++)) + posx; } int spriteIndex = rom.ReadByte(pos++); int spriteFlags = rom.ReadByte(pos++) ^ (horizontalFlip ? 0x40 : 0x00); DumpSpriteInternal(spx, spy, spriteIndex, spriteFlags, playerSprite, ref rectangle); } return(rectangle); }
static Rectangle DumpSprite(Rom rom, int posx, int posy, int spriteAddress, DirectBitmap tilesDest) { Rectangle rectangle = Rectangle.Empty; int pos = spriteAddress; pos = rom.ReadWord(pos); pos++; //skip sprite flags while (rom.ReadByte(pos) != 0x80) { int spx, spy; unchecked { spy = (sbyte)rom.ReadByte(pos++) + posy; spx = (sbyte)rom.ReadByte(pos++) + posx; } int spriteData = rom.ReadByte(pos++); int spriteIndex = (spriteData & 0x3F); DumpSpriteInternal(spx, spy, spriteIndex, spriteData, tilesDest, ref rectangle); } return(rectangle); }
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); }
public static void Dump16x16Tiles(Rom rom, int tileindexaddress, Bitmap gfx8, Graphics g, bool switchA, bool switchB) { for(int n = 0 ; n < 16 ; n++) { for(int i = 0 ; i < 8 ; i++) { int tileIndex = i + n * 8; tileIndex = Level.Switch(tileIndex, switchA, switchB); for(int k = 0 ; k < 2 ; k++) { for(int j = 0 ; j < 2 ; j++) { byte subTileIndex = rom.ReadByte(tileindexaddress + tileIndex * 4 + k * 2 + j); g.DrawImage(gfx8, new Rectangle(j * 8 + i * 16, k * 8 + n * 16, 8, 8), new Rectangle((subTileIndex % 16) * 8, ((subTileIndex) / 16) * 8, 8, 8), GraphicsUnit.Pixel); } } } } }
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 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 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 IEnumerable<byte> RLEDecompressTiles(Rom rom, int tilesdata) { while(true) { byte data = rom.ReadByte(tilesdata++); if((data & 0x80) == 0x80) { data = (byte)(data & 0x7F); byte rep = rom.ReadByte(tilesdata++); for(int j = 0 ; j <= rep ; j++) { yield return data; } } else { yield return data; } } }
public static IEnumerable<byte> RLEDecompressObjects(Rom rom, int enemiesData) { while(true) { byte data = rom.ReadByte(enemiesData++); yield return (byte)(data >> 4); yield return (byte)(data & 0xF); byte repeat = rom.ReadByte(enemiesData++); for(int i = 0 ; i < repeat ; i++) { yield return 0; yield return 0; } } }
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 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; } }