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 bool SaveChanges(Rom rom, int course, string filePath, out string errorMessage) { //rom expansion give new banks for level data rom.ExpandTo1MB(); SaveBlocksToRom(rom, course); if (!SaveObjectsToRom(rom, course, out errorMessage)) { return(false); } rom.FixCRC(); rom.Save(filePath); return(true); }
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 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 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"); }
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); }
void LoadToolStripMenuItemClick(object sender, EventArgs e) { OpenFileDialog openFileDialog1 = new OpenFileDialog(); openFileDialog1.Filter = "GB ROM Image (*.gb)|*.gb"; if (openFileDialog1.ShowDialog() == DialogResult.OK) { Rom newRom = new Rom(); newRom.Load(openFileDialog1.FileName); if (newRom.Title == "SUPERMARIOLAND3") { rom = newRom; LoadLevelCombobox(); Level.DumpBonusSprites(rom); Level.DumpPlayerSprite(rom); romFilePath = openFileDialog1.FileName; if (levelComboBox.SelectedIndex == 0) { LevelComboBoxSelectedIndexChanged(sender, e); } else { levelComboBox.SelectedIndex = 0; } toolboxToolStripMenuItem.Enabled = true; saveAsToolStripMenuItem.Enabled = true; levelComboBox.Visible = true; LevelPanel.Visible = true; } else { MessageBox.Show("Please select a valid WarioLand 1 rom", Text, MessageBoxButtons.OK, MessageBoxIcon.Error); } } }
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); } } } } }
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 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); }
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); }
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 bool SaveChanges(Rom rom, int course, string filePath, out string errorMessage) { //rom expansion give new banks for level data rom.ExpandTo1MB(); SaveBlocksToRom(rom, course); if(!SaveObjectsToRom(rom, course, out errorMessage)) { return false; } rom.FixCRC(); rom.Save(filePath); 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 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, 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; } }
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; } }
void LoadToolStripMenuItemClick(object sender, EventArgs e) { OpenFileDialog openFileDialog1 = new OpenFileDialog(); openFileDialog1.Filter = "GB ROM Image (*.gb)|*.gb"; if (openFileDialog1.ShowDialog() == DialogResult.OK) { Rom newRom = new Rom(); newRom.Load(openFileDialog1.FileName); if (newRom.Title == "SUPERMARIOLAND3") { rom = newRom; LoadLevelCombobox(); romFilePath = openFileDialog1.FileName; if(levelComboBox.SelectedIndex == 0) LevelComboBoxSelectedIndexChanged(sender, e); else levelComboBox.SelectedIndex = 0; saveToolStripMenuItem.Enabled = true; saveAsToolStripMenuItem.Enabled = true; levelComboBox.Visible = true; LevelPanel.Visible = true; tilesPictureBox.Visible = true; objectPictureBox.Visible = true; } else { MessageBox.Show("Please select a valid WarioLand 1 rom", Text, MessageBoxButtons.OK, MessageBoxIcon.Error); } } }