/// <summary> /// Packed Bitmap decoder. Calls from ConvertCustomToBitmap if the bitmap is packed, not snes. /// </summary> /// <param name="_buffer">Bitmap data</param> /// <param name="_palette">palette data</param> /// <param name="bpp">bits-per-pixel</param> /// <returns>Bitmap</returns> private static Bitmap PackedDecode(byte[] _buffer, Color[] _palette, int bpp) { IBitformat format = SnesGFX.AvaiableFormats[Settings.Default.Codec]; // let's get the width int width = bitmapInfo.Width; int height = _buffer.Length; if (format.Type == BitformatType.BITFORMAT_PACKED) { height = height * 8 / format.BitsPerPixel; } height /= width; //int packedtype; //if (!Options.PreserveWidth) //{ // width = 128; //} //if (Options.PackPacked) //{ // switch (Settings.Default.Codec) // { // case 4: // packedtype = 2; // height *= 4; // break; // case 5: // packedtype = 4; // height *= 2; // break; // case 6: // case 7: // packedtype = 8; // break; // default: // packedtype = 2; // height /= 4; // break; // } //} //else //{ // packedtype = 8; //} //height /= width; Bitmap bmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed); ColorPalette pal = bmp.Palette; int x = 0; while (x < _palette.Length) { pal.Entries[x] = _palette[x++]; } bmp.Palette = pal; byte[] output = format.Decode(_buffer); //ExGraphics.CodecInfo codec = new SuperFX.ExGraphics.CodecInfo(-1); //codec.Decode = true; //codec.SysName = string.Format("Pack{0}BPP", packedtype); //codec.Data = _buffer; //ExGraphics.StartCodec = codec; var bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed); IntPtr ptr = bmpData.Scan0; int bytes = Math.Abs(bmpData.Stride) * height; Marshal.Copy(output, 0, ptr, bytes); bmp.UnlockBits(bmpData); return(bmp); }
/// <summary> /// Rotina de conversão de Imagens para SNES Graphics /// </summary> private void Ripper_ProcessSystem(bool preview = false) { StringBuilder report = new StringBuilder(); try { if (!init) { return; } EnableOrDisable(false); InteragirFlag = true; Application.UseWaitCursor = true; new Thread(new ThreadStart(InteragirWindows)).Start(); if (textBox1.Tag == null) { goto End; } foreach (var file in (string[])textBox1.Tag) { string name = Regex.Replace(file.Replace(@"\", @"\\"), "[.]\\w{3,4}", "", Program.ropts); if (!Options.SaveOnImageFolder && name.LastIndexOf("\\") != -1) { name = name.Substring(name.LastIndexOf("\\") + 1); } Bitmap b = null; using (FileStream fs = new FileStream(file, FileMode.Open)) { b = new Bitmap(fs); fs.Close(); Program.bitmapInfo = b.Size; } double scale = (!checkBox3.Checked) ? 1 : (Int32.Parse(textBox2.Text) / 100D); byte[] result; byte[] palette; Color[] memPalette; string extension; byte[] mwl = null; Program.BitmapToRAWSNES(b, this.comboBox2.SelectedIndex + 2, scale, out result, out palette, out extension, out memPalette); b.Dispose(); IBitformat codec = SnesGFX.AvaiableFormats[comboBox1.SelectedIndex]; byte[] outputTilemap = null; if (Options.RemoveDuplicateTiles) { int baseSize = 8; int[] tilemap; bool[] flipx, flipy; int theWidth = codec.FixedWidth == 0 ? Program.bitmapInfo.Width : codec.FixedWidth; SNES.Tile[] tiles = SNES.Tile.FromBitmap(result, theWidth, baseSize); SNES.Tile[] aTiles = SNES.Tile.RemoveRepeatedBlocks(tiles, Options.RemoveFlippedTiles, out tilemap, out flipx, out flipy); int nTiles = aTiles.Length; // This is needed to some lines don't get cut-off. if (nTiles % 0x10 != 0) { nTiles += 0x10 - nTiles % 0x10; } SNES.Tile[] rTiles = new SNES.Tile[nTiles]; aTiles.CopyTo(rTiles, 0); for (int i = aTiles.Length; i < nTiles; ++i) { rTiles[i] = new SNES.Tile(new byte[baseSize * baseSize], baseSize); } result = SNES.Tile.ConvertToBitmap(rTiles, theWidth); int x_flip_count = 0; int y_flip_count = 0; int xy_flip_count = 0; for (int i = 0; i < flipx.Length; ++i) { if (flipx[i] && flipy[i]) { ++xy_flip_count; continue; } if (flipx[i]) { ++x_flip_count; continue; } if (flipy[i]) { ++y_flip_count; continue; } } string msg = @"Total Tiles Before Removing Duplicates: 0x{0:X4} Total Tiles After Removing Duplicates: 0x{1:X4} Total X Flipped Tiles: 0x{3:X4} Total Y Flipped Tiles: 0x{4:X4} Total X+Y Flipped Tiles: 0x{5:X4} Total Duplicated Tiles: 0x{6:X4} Total Removed: 0x{7:X4} Ratio: {2:F}%" ; msg = string.Format(CultureInfo.InvariantCulture, msg, tiles.Length, rTiles.Length, rTiles.Length / (double)tiles.Length * 100, x_flip_count, y_flip_count, xy_flip_count, tiles.Length - rTiles.Length - x_flip_count - y_flip_count - xy_flip_count, tiles.Length - rTiles.Length); report.AppendLine(name); report.AppendLine(msg); report.AppendLine(); if (((string[])textBox1.Tag).Length == 1) { MessageBox.Show(msg, "Anti-Tile Duplicate Results", MessageBoxButtons.OK, MessageBoxIcon.Information); } for (int i = 0; i < tilemap.Length; ++i) { tilemap[i] = (tilemap[i] + Options.OffsetTile) & 0x3FF; } if (rTiles.Length > 1024 && Options.TilemapOutput != 3) //Options.GenerateMap16) { MessageBox.Show("There are more than 1024 (0x400) tiles, thus " + "it's impossible to generate a SNES tilemap.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); goto End; } else if (Options.TilemapOutput == 0) { int size = tilemap.Length * 2; // 2kB o caralho //if (size % 0x800 != 0) //{ // size += 0x800 - size % 0x800; //} if (size <= 0x780) { if (size % 0x780 != 0) { size += 0x780 - size % 0x780; } } else { if (size % 0x800 != 0) { size += 0x800 - size % 0x800; } } outputTilemap = new byte[size]; for (int i = 0; i < tilemap.Length; ++i) { int item = tilemap[i] & 0x3FF; item += flipy[i] ? 0x8000 : 0; item += flipx[i] ? 0x4000 : 0; //int mIndex = i;// &~(0x20 | 0x40); //mIndex |= (i & 0x20) << 1; //mIndex |= (i & 0x40) >> 1; //mIndex <<= 1; int ii = (i & 0x0F) | ((i & ~0x0F) << 1); ii &= 0x3FF; ii |= i & ~0x3FF; ii |= (i & 0x200) >> 5; int mIndex = ((ii & 0x1F) << 1) | (ii & ~0x3F) | ((ii & 0x20) >> 5); mIndex <<= 1; if (mIndex >= size) { continue; } outputTilemap[mIndex] = (byte)(item & 0xFF); outputTilemap[mIndex + 1] = (byte)(item >> 8 & 0xFF); } } else if (Options.TilemapOutput == 1) { int size = tilemap.Length * 2; if (size % 0x800 != 0) { size += 0x800 - size % 0x800; } outputTilemap = new byte[size]; for (int i = 0; i < tilemap.Length; ++i) { int item = tilemap[i] & 0x3FF; item += flipy[i] ? 0x8000 : 0; item += flipx[i] ? 0x4000 : 0; //int mIndex = i;// &~(0x20 | 0x40); //mIndex |= (i & 0x20) << 1; //mIndex |= (i & 0x40) >> 1; //mIndex <<= 1; int ii = (i & 0x0F) | ((i & ~0x0F) << 1); ii &= 0x3FF; ii |= i & ~0x3FF; ii |= (i & 0x200) >> 5; int mIndex = ((ii & 0x1F) << 1) | (ii & ~0x3F) | ((ii & 0x20) >> 5); mIndex <<= 1; if (mIndex >= size) { continue; } outputTilemap[mIndex] = (byte)(item & 0xFF); outputTilemap[mIndex + 1] = (byte)(item >> 8 & 0xFF); } List <int[]> m16opt = new List <int[]>(); int[] m16index = new int[outputTilemap.Length >> 3]; for (int i = 0; i < outputTilemap.Length; i += 8) { int[] group = new int[4]; group[0] = outputTilemap[i] | (outputTilemap[i + 1] << 8); group[1] = outputTilemap[i + 2] | (outputTilemap[i + 3] << 8); group[2] = outputTilemap[i + 4] | (outputTilemap[i + 5] << 8); group[3] = outputTilemap[i + 6] | (outputTilemap[i + 7] << 8); int x = m16opt.FindIndex(p => p[0] == group[0] && p[1] == group[1] && p[2] == group[2] && p[3] == group[3]); if (x != -1) { m16index[i >> 3] = x; } else { m16index[i >> 3] = m16opt.Count; m16opt.Add(group); } } size = m16opt.Count * 8; if (size % 0x800 != 0) { size += 0x800 - size % 0x800; } outputTilemap = new byte[size]; for (int i = 0; i < m16opt.Count; ++i) { outputTilemap[(i << 3) + 0] = (byte)(m16opt[i][0]); outputTilemap[(i << 3) + 1] = (byte)(m16opt[i][0] >> 8); outputTilemap[(i << 3) + 2] = (byte)(m16opt[i][1]); outputTilemap[(i << 3) + 3] = (byte)(m16opt[i][1] >> 8); outputTilemap[(i << 3) + 4] = (byte)(m16opt[i][2]); outputTilemap[(i << 3) + 5] = (byte)(m16opt[i][2] >> 8); outputTilemap[(i << 3) + 6] = (byte)(m16opt[i][3]); outputTilemap[(i << 3) + 7] = (byte)(m16opt[i][3] >> 8); } // assuming the map16 will get stored at page 0x42... //FuSoYa: //"Base mwl file attached. This file is already set to use a custom //palette and custom BG. Insert the BG tilemap at offset 0xD6 (0x800 //bytes, 16 bit values little endian). The upper 4 bits in the byte at //offset 0xCE should be set to the Map16 bank you want to use (0-3), //while the lower 4 bits should not be changed. Offset 0x8E8 is the //palette table (SNES format, 16 bit values little endian, 0x202 bytes //as there's an extra entry at the end for the back area color)." if (rTiles.Length > 0x300) { MessageBox.Show("There are more than 768 (0x300) tiles. It means it won't fit even if you " + "use all LM slots (FG1-3; BG1-3). Conversion aborted.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); goto End; } mwl = Resources._base; // we need to deinterleave the gfx "blocks" or it will look weird on LM int w = Program.bitmapInfo.Width; int h = Program.bitmapInfo.Height; int h2 = h; if (w % 128 != 0) { w += 128 - w % 128; } if (h % 256 != 0) { h += 256 - h % 256; } if (w > 512 || h > 512) { MessageBox.Show("The output image (" + w + "x" + h + ") is larger than 512x512! Conversion aborted.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); goto End; } for (int y = 0; y < h; y += 16) { for (int x = 0; x < w; x += 16) { // destination: there are 0x400 blocks, each one of 16x16 // format is, well: // XYyyyyxxxx int dest = (x >> 4 & 15) | ((y >> 4 & 15) << 4) | ((x >> 8 & 1) << 9) | ((y >> 8 & 1) << 8); dest <<= 1; dest += 0xD6; // source: this one is more fun. // format: // ...PP pyyyYxxx // and it's offset based. // oh well. if (y >> 4 >= h2 >> 4) { continue; } int fakeY = (y >> 4) + (x >> 7) * (h2 >> 4); int source = (x >> 4 & 7) | ((fakeY & 15) << 4) | ((fakeY >> 4 & 1) << 3) | ((fakeY >> 5) << 8); // | ((offset >> 3 & 7) << 4) | ((offset >> 6 & 1) << 3) | ((offset >> 7) << 7); if (source >= m16index.Length) { continue; } int m16 = 0x0200 + m16index[source]; mwl[dest] = (byte)m16; mwl[dest + 1] = (byte)(m16 >> 8); } } byte[] mw3 = Program.PaletteToMw3(memPalette, 256); mw3[512] = mw3[0]; mw3[513] = mw3[1]; mw3[0] = 0; mw3[1] = 0; mw3.CopyTo(mwl, 0x8E8); } else if (Options.TilemapOutput == 2) { // we need to deinterleave the gfx "blocks" or it will look weird on LM int w = Program.bitmapInfo.Width; int h = Program.bitmapInfo.Height; int h2 = h; if (w % 128 != 0) { w += 128 - w % 128; } if (h % 256 != 0) { h += 256 - h % 256; } if (w > 512 || h > 512) { MessageBox.Show("The output image (" + w + "x" + h + ") is larger than 512x512! Conversion aborted.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); goto End; } //brrrrrrrrrrrrrrrrrrrrrrrrrr! if (w <= 256 && h <= 256) { //outputTilemap = new byte[32 * 32 * 2]; // attention TO DO outputTilemap = new byte[0x780]; } else if (w > 256 && h <= 256) { outputTilemap = new byte[64 * 32 * 2]; } else { outputTilemap = new byte[64 * 64 * 2]; } for (int y = 0; y < h; y += 8) { for (int x = 0; x < w; x += 8) { // destination: there are 0x1000 blocks, each one of 8x8 // format is, well: // XYyy yyyx xxxx int dest = (x >> 3 & 31) | ((y >> 3 & 31) << 5) | ((x >> 8 & 1) << 10) | ((y >> 8 & 1) << 11); dest <<= 1; // pp pppp xxxx if (y >> 3 >= h2 >> 3) { continue; } int fakeY = (y >> 3) + (x >> 7) * (h2 >> 3); int source = (x >> 3 & 15) | (fakeY << 4); if (source >= tilemap.Length) { continue; } int item = tilemap[source] & 0x3FF; item += flipy[source] ? 0x8000 : 0; item += flipx[source] ? 0x4000 : 0; outputTilemap[dest] = (byte)(item & 0xFF); outputTilemap[dest + 1] = (byte)(item >> 8 & 0xFF); } } } else if (Options.TilemapOutput == 3 || Options.TilemapOutput == 4) { // mode 7 int w = Program.bitmapInfo.Width; int h = Program.bitmapInfo.Height; int h2 = h; if (w % 128 != 0) { w += 128 - w % 128; } if (h % 256 != 0) { h += 256 - h % 256; } if (w > 1024 || h > 1024) { MessageBox.Show("The output image (" + w + "x" + h + ") is larger than 1024x1024! Conversion aborted.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); goto End; } if (rTiles.Length > 0x100) { MessageBox.Show("There are more than 256 (0x100) tiles. It won't fit on a Mode 7 Character Data, " + "which is limited to 128x128. Conversion aborted.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); goto End; } outputTilemap = new byte[128 * 128]; for (int y = 0; y < h; y += 8) { for (int x = 0; x < w; x += 8) { // destination: there are 0x1000 blocks, each one of 8x8 // format is, well: // XYyy yyyx xxxx // format: // yy yyyy yxxx xxxx int dest = (x >> 3 & 1023) | ((y >> 3 & 1023) << 7); // pp pppp xxxx if (y >> 3 >= h2 >> 3) { continue; } int fakeY = (y >> 3) + (x >> 7) * (h2 >> 3); int source = (x >> 3 & 15) | (fakeY << 4); if (source >= tilemap.Length) { continue; } if (flipy[source] || flipx[source]) { MessageBox.Show("Mode 7 can't flip tiles! Please disable \"Remove Flipped Tiles\"" + "option and try again. Conversion aborted.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); goto End; } outputTilemap[dest] = (byte)(tilemap[source] & 0xFF); } } } } byte[] output = codec.Encode(result); if (!preview) { int error_count = 0; while (true) { try { if (!Options.SplitOutput) { if (Options.TilemapOutput == 4) { // kk interleave mode byte[] final = new byte[0x8000]; for (int x = 0, y = 0; x < 0x8000; x += 2, ++y) { final[x + 0] = outputTilemap[y]; if (y < output.Length) { final[x + 1] = output[y]; } else { final[x + 1] = 0; } } File.WriteAllBytes(name + ".bin", final); } else { File.WriteAllBytes(name + ".bin", output); } } else { if (Options.TilemapOutput == 4) { MessageBox.Show("Can't split output with Mode 7 Interleaved Tilemap!", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); goto End; } int maxSize = 1 << (Options.SplitOutputIndex + 9); int size = output.Length; int index = 0; int part = 0; while (index < size) { byte[] area = new byte[maxSize]; if (index + maxSize < size) { Array.Copy(output, index, area, 0, maxSize); } else { Array.Copy(output, index, area, 0, size - index); } File.WriteAllBytes(name + "_part_" + part.ToString("X2") + ".bin", area); index += maxSize; ++part; } } if (palette.Length != 0) { File.WriteAllBytes(name + extension, palette); } if (outputTilemap != null && Options.TilemapOutput != 4) { File.WriteAllBytes(name + "_map16.bin", outputTilemap); } if (mwl != null) { File.WriteAllBytes(name + ".mwl", mwl); } break; } catch (Exception e) { if (error_count++ >= 6) { MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); goto End; } } } } else { Bitmap graphics = Program.ConvertCustomToBitmap(output, memPalette, codec.BitsPerPixel); if (Program.view == null) { Program.view = new Preview(graphics, new Size(256, 256)); Program.view.Owner = this; Program.view.Show(); } else if (Program.view.IsDisposed) { Program.view = new Preview(graphics, new Size(256, 256)); Program.view.Owner = this; Program.view.Show(); } else { Program.view.CurrrentImage = graphics; } } } } catch (NullReferenceException) { MessageBox.Show("This image has too many colors to fit on configuration. " + "Please enable \"optimize image\" option to reduce the number of colors.", "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); } catch (Exception e) { MessageBox.Show(e.Message, "Error", MessageBoxButtons.OK, MessageBoxIcon.Warning); goto End; } End: Application.UseWaitCursor = false; EnableOrDisable(true); InteragirFlag = false; File.WriteAllText("convertlog.txt", report.ToString()); }
/// <summary> /// Converts a SNES/PACKED BIT-MAP to Bitmap. /// </summary> /// <param name="_buffer">The raw data</param> /// <param name="_palette">Palette</param> /// <param name="bpp">bits-per-pixel</param> /// <returns>The Bitmap</returns> public static Bitmap ConvertCustomToBitmap(byte[] _buffer, Color[] _palette, int bpp) { if (SnesGFX.AvaiableFormats[Settings.Default.Codec].Type != BitformatType.BITFORMAT_PLANAR) { return(PackedDecode(_buffer, _palette, bpp)); } long a8x8 = 0; long size = _buffer.Length; a8x8 = size / ((64 * bpp) / 8); int height = (int)((a8x8 / 16) * 8); int width = (int)((a8x8 > 16) ? 128 : a8x8 * 8); if (height == 0) { height = 0; } if (height % 8 != 0) { height += 8 - (height % 8); } Bitmap bmp = new Bitmap(width, height, PixelFormat.Format8bppIndexed); ColorPalette pal = bmp.Palette; int x = 0; while (x < _palette.Length) { pal.Entries[x] = _palette[x++]; } if (Options.AllowTransparency) { pal.Entries[0] = Color.Transparent; } bmp.Palette = pal; IBitformat format = SnesGFX.AvaiableFormats[Settings.Default.Codec]; byte[] output = format.Decode(_buffer); var bmpData = bmp.LockBits(new Rectangle(0, 0, width, height), ImageLockMode.ReadWrite, PixelFormat.Format8bppIndexed); IntPtr ptr = bmpData.Scan0; int bytes = Math.Abs(bmpData.Stride) * height; //fixed (byte* ptr2 = output) //{ // byte[] rgbValues = SuperFX.ExGraphics.a8x8ToTilemap(ptr2, width, codec.output.Length); // Marshal.Copy(rgbValues, 0, ptr, bytes); // rgbValues = null; //} Marshal.Copy(output, 0, ptr, bytes); bmp.UnlockBits(bmpData); return(bmp); }