diff --git a/pk3DS/3DS/BCLIM.cs b/pk3DS/3DS/BCLIM.cs
new file mode 100644
index 0000000..d5b9257
--- /dev/null
+++ b/pk3DS/3DS/BCLIM.cs
@@ -0,0 +1,754 @@
+using System;
+using System.Drawing;
+using System.Drawing.Imaging;
+using System.IO;
+
+namespace CTR
+{
+ class BCLIM
+ {
+ internal static void openFile(string path, bool autosave = false, bool crop = true, char format = 'X')
+ {
+ // Handle file
+ if (!File.Exists(path)) throw new Exception("Can only accept files, not folders");
+ string ext = Path.GetExtension(path);
+ if (ext == ".png")
+ makeBCLIM(path, format);
+ else if (ext == ".bin" || ext == ".bclim")
+ makeBMP(path, autosave, crop);
+ }
+
+ internal static Image makeBCLIM(string path, char fc)
+ {
+ byte[] byteArray = File.ReadAllBytes(path);
+ using (Stream BitmapStream = new MemoryStream(byteArray)) // Open the file, even if it is in use.
+ {
+ Image img = Image.FromStream(BitmapStream);
+ Bitmap mBitmap = new Bitmap(img);
+ // Pixel format acquired. Now to determine how we want to save the data.
+ MemoryStream ms = new MemoryStream();
+ int bclimformat = 7; // Init to default (for X)
+
+ if (fc == 'X')
+ write16BitColorPalette(mBitmap, ref ms);
+ else
+ {
+ bclimformat = Convert.ToInt16(fc.ToString(), 16);
+ try
+ {
+ writeGeneric(bclimformat, mBitmap, ref ms);
+ }
+ catch (Exception e)
+ {
+ System.Media.SystemSounds.Beep.Play();
+ System.Diagnostics.Debug.WriteLine(e.ToString());
+ }
+ }
+
+ long datalength = ms.Length;
+ // Write the CLIM + imag data.
+ using (BinaryWriter bw = new BinaryWriter(ms))
+ {
+ bw.Write((uint)0x4D494C43); // CLIM
+ bw.Write((ushort)0xFEFF); // BOM
+ bw.Write((uint)0x14);
+ bw.Write((ushort)0x0202); // 2 2
+ bw.Write((uint)(datalength+0x28));
+ bw.Write((uint)1);
+ bw.Write((uint)0x67616D69);
+ bw.Write((uint)0x10);
+ bw.Write((ushort)mBitmap.Width);
+ bw.Write((ushort)mBitmap.Height);
+ bw.Write((uint)bclimformat);
+ bw.Write((uint)datalength);
+ }
+ string fp = Path.GetFileNameWithoutExtension(path);
+ fp = "new_" + fp.Substring(fp.IndexOf('_') + 1);
+ string pp = Path.GetDirectoryName(path);
+ string newPath = Path.Combine(pp, fp + ".bclim");
+ File.WriteAllBytes(newPath, ms.ToArray());
+
+ return makeBMP(newPath);
+ }
+ }
+ internal static Image makeBMP(string path, bool autosave = false, bool crop = true)
+ {
+ CLIM bclim = analyze(path);
+ if (bclim.Magic != 0x4D494C43)
+ {
+ System.Media.SystemSounds.Beep.Play();
+ return null;
+ }
+
+ // Interpret data.
+ int f = bclim.FileFormat;
+ if (f > 13)
+ {
+ System.Media.SystemSounds.Exclamation.Play();
+ return null;
+ }
+
+ Bitmap img;
+ if (f == 7 && BitConverter.ToUInt16(bclim.Data, 0) == 2)
+ // PKM XY Format 7 (Color Palette)
+ img = getIMG_XY7(bclim);
+ else if (f == 10 || f == 11)
+ img = getIMG_ETC(bclim);
+ else
+ img = getIMG(bclim);
+
+ Rectangle cropRect = new Rectangle(0, 0, bclim.Width, bclim.Height);
+ Bitmap CropBMP = new Bitmap(cropRect.Width, cropRect.Height);
+ using (Graphics g = Graphics.FromImage(CropBMP))
+ {
+ g.DrawImage(img,
+ new Rectangle(0, 0, CropBMP.Width, CropBMP.Height),
+ cropRect,
+ GraphicsUnit.Pixel);
+ }
+ if (!autosave) return !crop ? img : CropBMP;
+
+ using (MemoryStream ms = new MemoryStream())
+ {
+ //error will throw from here
+ CropBMP.Save(ms, ImageFormat.Png);
+ byte[] data = ms.ToArray();
+ File.WriteAllBytes(bclim.FilePath + "\\" + bclim.FileName + ".png", data);
+ }
+ return !crop ? img : CropBMP;
+ }
+ // Bitmap Data Writing
+ internal static Bitmap getIMG(CLIM bclim)
+ {
+ // New Image
+ Bitmap img = new Bitmap(nlpo2(gcm(bclim.Width,8)), nlpo2(gcm(bclim.Height,8)));
+ int f = bclim.FileFormat;
+ int area = img.Width * img.Height;
+ if (f == 9 && area > bclim.Data.Length/4)
+ {
+ img = new Bitmap(gcm(bclim.Width, 8), gcm(bclim.Height, 8));
+ area = img.Width * img.Height;
+ }
+ // Coordinates
+ // Colors
+ // Tiles Per Width
+ int p = gcm(img.Width, 8) / 8;
+ if (p == 0) p = 1;
+
+ // Build Image
+ using (Stream BitmapStream = new MemoryStream(bclim.Data))
+ using (BinaryReader br = new BinaryReader(BitmapStream))
+ for (uint i = 0; i < area; i++) // for every pixel
+ {
+ uint x;
+ uint y;
+ d2xy(i % 64, out x, out y);
+ uint tile = i / 64;
+
+ // Shift Tile Coordinate into Tilemap
+ x += (uint)(tile % p) * 8;
+ y += (uint)(tile / p) * 8;
+
+ // Get Color
+ Color c;
+ switch (f)
+ {
+ case 0x0: // L8 // 8bit/1 byte
+ case 0x1: // A8
+ case 0x2: // LA4
+ c = DecodeColor(br.ReadByte(), f);
+ break;
+ case 0x3: // LA8 // 16bit/2 byte
+ case 0x4: // HILO8
+ case 0x5: // RGB565
+ case 0x8: // RGBA4444
+ case 0x7: // RGBA5551
+ c = DecodeColor(br.ReadUInt16(), f);
+ break;
+ case 0x6: // RGB8: // 24bit
+ byte[] data = br.ReadBytes(3); Array.Resize(ref data, 4);
+ c = DecodeColor(BitConverter.ToUInt32(data, 0), f);
+ break;
+ case 0x9: // RGBA8888
+ c = DecodeColor(br.ReadUInt32(), f);
+ break;
+ case 0xC: // L4
+ case 0xD: // A4 // 4bit - Do 2 pixels at a time.
+ uint val = br.ReadByte();
+ img.SetPixel((int)x, (int)y, DecodeColor(val & 0xF, f)); // lowest bits for the low pixel
+ i++; x++;
+ c = DecodeColor(val >> 4, f); // highest bits for the high pixel
+ break;
+ default: throw new Exception("Invalid FileFormat.");
+ }
+ img.SetPixel((int)x, (int)y, c);
+ }
+ return img;
+ }
+ internal static Bitmap getIMG_XY7(CLIM bclim)
+ {
+ Bitmap img = new Bitmap(bclim.BaseSize, bclim.BaseSize);
+ using (Stream BitmapStream = new MemoryStream(bclim.Data))
+ using (BinaryReader br = new BinaryReader(BitmapStream))
+ {
+ // Fetch Color stuff.
+ if (br.ReadUInt16() != 2) return null;
+ ushort colors = br.ReadUInt16();
+ Color[] ca = new Color[colors];
+ for (int i = 0; i < colors; i++)
+ ca[i] = DecodeColor(br.ReadUInt16(), 7);
+
+ // Coordinates
+ // Colors
+ // Tiles Per Width
+ int p = gcm(img.Width, 8) / 8;
+ if (p == 0) p = 1;
+
+ for (uint i = 0; i < bclim.BaseSize * bclim.BaseSize; i++) // for every pixel
+ {
+ uint x;
+ uint y;
+ d2xy(i % 64, out x, out y);
+ uint tile = i / 64;
+
+ // Shift Tile Coordinate into Tilemap
+ x += (uint)(tile % p) * 8;
+ y += (uint)(tile / p) * 8;
+
+ byte val = br.ReadByte();
+ if (colors <= 0x10) // Handle 2 pixels at a time
+ {
+ img.SetPixel((int)x, (int)y, ca[val >> 4]);
+ x++; i++; val &= 0xF;
+ img.SetPixel((int)x, (int)y, ca[val]);
+ }
+ else //1bpp instead of .5, handle 2 pixels at a time the same way for no reason
+ {
+ img.SetPixel((int)x, (int)y, ca[val]);
+ x++; i++; val = br.ReadByte();
+ img.SetPixel((int)x, (int)y, ca[val]);
+ }
+ }
+ }
+ return img;
+ }
+ internal static Bitmap getIMG_ETC(CLIM bclim)
+ {
+ //int format = bclim.FileFormat;
+ //if (format != 10 && format != 11)
+ // return null;
+
+ //byte[] data = ETC1_Decompress(bclim.Data, bclim.Width, bclim.Height, format + 2);
+ //Bitmap img = new Bitmap(bclim.Width, bclim.Height);
+
+ //for (int i = 0; i < bclim.Width * bclim.Height; i++) // for every pixel
+ //{
+ // Color c = DecodeColor(BitConverter.ToUInt32(data, i * 4), 9);
+ // int x = i % bclim.Width;
+ // int y = i / bclim.Width;
+ // img.SetPixel(x, y, c);
+ //}
+ //return img;
+ return null;
+ }
+
+ // BCLIM Data Writing
+ internal static int write16BitColorPalette(Bitmap img, ref MemoryStream ms)
+ {
+ using (Stream pixelcolors = new MemoryStream())
+ using (BinaryWriter bz = new BinaryWriter(pixelcolors))
+ {
+ // Set up our basis.
+ bool under16colors = false;
+ int colors = getColorCount(img);
+ Color[] pcs = new Color[colors];
+ if (colors < 16) under16colors = true;
+ uint div = 1;
+ if (under16colors)
+ div = 2;
+
+ if (colors > 70) throw new Exception("Too many colors");
+
+ // Set up a new reverse image to build into.
+ int w = gcm(img.Width, 8);
+ int h = gcm(img.Height, 8);
+ w = Math.Max(nlpo2(w), nlpo2(h));
+ h = w;
+ byte[] pixelarray = new Byte[w * h];
+
+ const int colorformat = 2;
+ int ctr = 1;
+
+ pcs[0] = Color.FromArgb(0, 0xFF, 0xFF, 0xFF);
+
+ int p = gcm(w, 8) / 8;
+ if (p == 0) p = 1;
+ int d = 0;
+ for (uint i = 0; i < pixelarray.Length; i++)
+ {
+ d = (int)(i / div);
+ // Get Tile Coordinate
+ uint x;
+ uint y;
+ d2xy(i % 64, out x, out y);
+
+ // Get Shift Tile
+ uint tile = i / 64;
+
+ // Shift Tile Coordinate into Tilemap
+ x += (uint)(tile % p) * 8;
+ y += (uint)(tile / p) * 8;
+ if (x >= img.Width || y >= img.Height) // Don't try to access any pixel data outside of our bounds.
+ { i++; continue; } // Goto next tile.
+
+ // Get Color of Pixel
+ Color c = img.GetPixel((int)x, (int)y);
+
+ // Color Table Building Logic
+ int index = Array.IndexOf(pcs, c);
+ if (c.A == 0) index = 0;
+ if (index < 0) // If new color
+ { pcs[ctr] = c; index = ctr; ctr++; } // Add it to color list
+
+ // Add pixel to pixeldata
+ if (under16colors) index = index << 4;
+ pixelarray[i / div] = (byte)index;
+ if (!under16colors) continue;
+
+ c = img.GetPixel((int)x + 1, (int)y);
+ index = Array.IndexOf(pcs, c);
+ if (c.A == 0) index = 0;
+ if (index < 0) // If new color
+ { pcs[ctr] = c; index = ctr; ctr++; }
+ pixelarray[i / div] |= (byte)(index);
+ i++;
+ }
+
+ // Write Intro
+ bz.Write((ushort)colorformat); bz.Write((ushort)ctr);
+ // Write Colors
+ for (int i = 0; i < ctr; i++)
+ bz.Write(GetRGBA5551(pcs[i])); // Write byte array.
+ // Write Pixel Data
+ for (uint i = 0; i < d; i++)
+ bz.Write(pixelarray[i]);
+ // Write Padding
+ while (pixelcolors.Length < nlpo2((int)pixelcolors.Length))
+ bz.Write((byte)0);
+ // Copy to main CLIM.
+ pixelcolors.Position = 0; pixelcolors.CopyTo(ms);
+ }
+ return 7;
+ }
+ internal static void writeGeneric(int format, Bitmap img, ref MemoryStream ms)
+ {
+ int w = img.Width;
+ int h = img.Height;
+
+ // square format. Yay.
+ w = h = Math.Max(nlpo2(w),nlpo2(h));
+ if (format == 0 && Math.Min(img.Width, img.Height) > 64)
+ {
+ w = nlpo2(img.Width);
+ h = nlpo2(img.Height);
+ }
+ using (MemoryStream mz = new MemoryStream())
+ using (BinaryWriter bz = new BinaryWriter(mz))
+ {
+ int p = gcm(w, 8) / 8;
+ if (p == 0) p = 1;
+ for (uint i = 0; i < w * h; i++)
+ {
+ uint x;
+ uint y;
+ d2xy(i % 64, out x, out y);
+
+ // Get Shift Tile
+ uint tile = i / 64;
+
+ // Shift Tile Coordinate into Tilemap
+ x += (uint)(tile % p) * 8;
+ y += (uint)(tile / p) * 8;
+
+ // Don't write data
+ Color c;
+ if (x >= img.Width || y >= img.Height)
+ { c = Color.FromArgb(0, 0, 0, 0); }
+ else
+ { c = img.GetPixel((int)x, (int)y); if (c.A == 0) c = Color.FromArgb(0, 86, 86, 86); }
+
+ switch (format)
+ {
+ case 0: bz.Write(GetL8(c)); break; // L8
+ case 1: bz.Write(GetA8(c)); break; // A8
+ case 2: bz.Write(GetLA4(c)); break; // LA4(4)
+ case 3: bz.Write(GetLA8(c)); break; // LA8(8)
+ case 4: bz.Write(GetHILO8(c)); break; // HILO8
+ case 5: bz.Write(GetRGB565(c)); break; // RGB565
+ case 6:
+ {
+ bz.Write(c.B);
+ bz.Write(c.G);
+ bz.Write(c.R); break;
+ }
+ case 7: bz.Write(GetRGBA5551(c)); break; // RGBA5551
+ case 8: bz.Write(GetRGBA4444(c)); break; // RGBA4444
+ case 9: bz.Write(GetRGBA8888(c)); break; // RGBA8
+ case 10: throw new Exception("ETC1 not supported.");
+ case 11: throw new Exception("ETC1A4 not supported.");
+ case 12:
+ {
+ byte val = (byte)(GetL8(c) / 0x11); // First Pix // L4
+ { c = img.GetPixel((int)x, (int)y); if (c.A == 0) c = Color.FromArgb(0, 0, 0, 0); }
+ val |= (byte)((GetL8(c) / 0x11) << 4); i++;
+ bz.Write(val); break;
+ }
+ case 13:
+ {
+ byte val = (byte)(GetA8(c) / 0x11); // First Pix // L4
+ { c = img.GetPixel((int)x, (int)y); }
+ val |= (byte)((GetA8(c) / 0x11) << 4); i++;
+ bz.Write(val); break;
+ }
+ }
+ }
+ while (mz.Length < nlpo2((int)mz.Length)) // pad
+ bz.Write((byte)0);
+ mz.Position = 0;
+ mz.CopyTo(ms);
+ }
+ }
+ internal static int getColorCount(Bitmap img)
+ {
+ Color[] colors = new Color[img.Width * img.Height];
+ int colorct = 1;
+
+ for (int i = 0; i < colors.Length; i++)
+ {
+ Color c = img.GetPixel(i % img.Width, i / img.Width);
+ int index = Array.IndexOf(colors, c);
+ if (c.A == 0) index = 0;
+ if (index >= 0) continue;
+
+ colors[colorct] = c;
+ colorct++;
+ }
+ return colorct;
+ }
+
+
+ internal static int[] Convert5To8 = { 0x00,0x08,0x10,0x18,0x20,0x29,0x31,0x39,
+ 0x41,0x4A,0x52,0x5A,0x62,0x6A,0x73,0x7B,
+ 0x83,0x8B,0x94,0x9C,0xA4,0xAC,0xB4,0xBD,
+ 0xC5,0xCD,0xD5,0xDE,0xE6,0xEE,0xF6,0xFF };
+
+ private static Color DecodeColor(uint val, int format)
+ {
+ int alpha = 0xFF, red, green, blue;
+ switch (format)
+ {
+ case 0: // L8
+ return Color.FromArgb(alpha, (byte)val, (byte)val, (byte)val);
+ case 1: // A8
+ return Color.FromArgb((byte)val, alpha, alpha, alpha);
+ case 2: // LA4
+ red = (byte)(val >> 4);
+ alpha = (byte)(val & 0x0F);
+ return Color.FromArgb(alpha, red, red, red);
+ case 3: // LA8
+ red = (byte)((val >> 8 & 0xFF));
+ alpha = (byte)(val & 0xFF);
+ return Color.FromArgb(alpha, red, red, red);
+ case 4: // HILO8
+ red = (byte)(val >> 8);
+ green = (byte)(val & 0xFF);
+ return Color.FromArgb(alpha, red, green, 0xFF);
+ case 5: // RGB565
+ red = Convert5To8[(val >> 11) & 0x1F];
+ green = (byte)(((val >> 5) & 0x3F) * 4);
+ blue = Convert5To8[val & 0x1F];
+ return Color.FromArgb(alpha, red, green, blue);
+ case 6: // RGB8
+ red = (byte)((val >> 16) & 0xFF);
+ green = (byte)((val >> 8) & 0xFF);
+ blue = (byte)(val & 0xFF);
+ return Color.FromArgb(alpha, red, green, blue);
+ case 7: // RGBA5551
+ red = Convert5To8[(val >> 11) & 0x1F];
+ green = Convert5To8[(val >> 6) & 0x1F];
+ blue = Convert5To8[(val >> 1) & 0x1F];
+ alpha = (val & 0x0001) == 1 ? 0xFF : 0x00;
+ return Color.FromArgb(alpha, red, green, blue);
+ case 8: // RGBA4444
+ alpha = (byte)(0x11 * (val & 0xf));
+ red = (byte)(0x11 * ((val >> 12) & 0xf));
+ green = (byte)(0x11 * ((val >> 8) & 0xf));
+ blue = (byte)(0x11 * ((val >> 4) & 0xf));
+ return Color.FromArgb(alpha, red, green, blue);
+ case 9: // RGBA8888
+ red = (byte)((val >> 24) & 0xFF);
+ green = (byte)((val >> 16) & 0xFF);
+ blue = (byte)((val >> 8) & 0xFF);
+ alpha = (byte)(val & 0xFF);
+ return Color.FromArgb(alpha, red, green, blue);
+ // case 10:
+ // case 11:
+ case 12: // L4
+ return Color.FromArgb(alpha, (byte)(val * 0x11), (byte)(val * 0x11), (byte)(val * 0x11));
+ case 13: // A4
+ return Color.FromArgb((byte)(val * 0x11), alpha, alpha, alpha);
+ default:
+ return Color.White;
+ }
+ }
+
+ // Color Conversion
+ internal static byte GetL8(Color c)
+ {
+ byte red = c.R;
+ byte green = c.G;
+ byte blue = c.B;
+ // Luma (Y’) = 0.299 R’ + 0.587 G’ + 0.114 B’ from wikipedia
+ return (byte)(((0x4CB2 * red + 0x9691 * green + 0x1D3E * blue) >> 16) & 0xFF);
+ } // L8
+ internal static byte GetA8(Color c)
+ {
+ return c.A;
+ } // A8
+ internal static byte GetLA4(Color c)
+ {
+ return (byte)((c.A / 0x11) + (c.R / 0x11) << 4);
+ } // LA4
+ internal static ushort GetLA8(Color c)
+ {
+ return (ushort)((c.A) + ((c.R) << 8));
+ } // LA8
+ internal static ushort GetHILO8(Color c)
+ {
+ return (ushort)((c.G) + ((c.R) << 8));
+ } // HILO8
+ internal static ushort GetRGB565(Color c)
+ {
+ int val = 0;
+ // val += c.A >> 8; // unused
+ val += convert8to5(c.B) >> 3;
+ val += (c.G >> 2) << 5;
+ val += convert8to5(c.R) << 10;
+ return (ushort)val;
+ } // RGB565
+ // RGB8
+ internal static ushort GetRGBA5551(Color c)
+ {
+ int val = 0;
+ val += (byte)(c.A > 0x80 ? 1 : 0);
+ val += convert8to5(c.R) << 11;
+ val += convert8to5(c.G) << 6;
+ val += convert8to5(c.B) << 1;
+ ushort v = (ushort)val;
+
+ return v;
+ }// RGBA5551
+ internal static ushort GetRGBA4444(Color c)
+ {
+ int val = 0;
+ val += (c.A / 0x11);
+ val += ((c.B / 0x11) << 4);
+ val += ((c.G / 0x11) << 8);
+ val += ((c.R / 0x11) << 12);
+ return (ushort)val;
+ }// RGBA4444
+ internal static uint GetRGBA8888(Color c) // RGBA8888
+ {
+ uint val = 0;
+ val += c.A;
+ val += (uint)(c.B << 8);
+ val += (uint)(c.G << 16);
+ val += (uint)(c.R << 24);
+ return val;
+ }
+
+ // Unit Conversion
+ internal static byte convert8to5(int colorval)
+ {
+
+ byte[] Convert8to5 = { 0x00,0x08,0x10,0x18,0x20,0x29,0x31,0x39,
+ 0x41,0x4A,0x52,0x5A,0x62,0x6A,0x73,0x7B,
+ 0x83,0x8B,0x94,0x9C,0xA4,0xAC,0xB4,0xBD,
+ 0xC5,0xCD,0xD5,0xDE,0xE6,0xEE,0xF6,0xFF };
+ byte i = 0;
+ while (colorval > Convert8to5[i]) i++;
+ return i;
+ }
+ internal static UInt32 DM2X(UInt32 code)
+ {
+ return C11(code >> 0);
+ }
+ internal static UInt32 DM2Y(UInt32 code)
+ {
+ return C11(code >> 1);
+ }
+ internal static UInt32 C11(UInt32 x)
+ {
+ x &= 0x55555555; // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0
+ x = (x ^ (x >> 1)) & 0x33333333; // x = --fe --dc --ba --98 --76 --54 --32 --10
+ x = (x ^ (x >> 2)) & 0x0f0f0f0f; // x = ---- fedc ---- ba98 ---- 7654 ---- 3210
+ x = (x ^ (x >> 4)) & 0x00ff00ff; // x = ---- ---- fedc ba98 ---- ---- 7654 3210
+ x = (x ^ (x >> 8)) & 0x0000ffff; // x = ---- ---- ---- ---- fedc ba98 7654 3210
+ return x;
+ }
+
+ ///
+ /// Greatest common multiple (to round up)
+ ///
+ /// Number to round-up.
+ /// Multiple to round-up to.
+ /// Rounded up number.
+ internal static int gcm(int n, int m)
+ {
+ return ((n + m - 1) / m) * m;
+ }
+ ///
+ /// Next Largest Power of 2
+ ///
+ /// Input to round up to next 2^n
+ /// 2^n > x && x > 2^(n-1)
+ internal static int nlpo2(int x)
+ {
+ x--; // comment out to always take the next biggest power of two, even if x is already a power of two
+ x |= (x >> 1);
+ x |= (x >> 2);
+ x |= (x >> 4);
+ x |= (x >> 8);
+ x |= (x >> 16);
+ return (x+1);
+ }
+
+ // Morton Translation
+ ///
+ /// Combines X/Y Coordinates to a decimal ordinate.
+ ///
+ ///
+ ///
+ ///
+ internal static uint xy2d(uint x, uint y)
+ {
+ x &= 0x0000ffff;
+ y &= 0x0000ffff;
+ x |= (x << 8);
+ y |= (y << 8);
+ x &= 0x00ff00ff;
+ y &= 0x00ff00ff;
+ x |= (x << 4);
+ y |= (y << 4);
+ x &= 0x0f0f0f0f;
+ y &= 0x0f0f0f0f;
+ x |= (x << 2);
+ y |= (y << 2);
+ x &= 0x33333333;
+ y &= 0x33333333;
+ x |= (x << 1);
+ y |= (y << 1);
+ x &= 0x55555555;
+ y &= 0x55555555;
+ return x | (y << 1);
+ }
+ ///
+ /// Decimal Ordinate In to X / Y Coordinate Out
+ ///
+ /// Loop integer which will be decoded to X/Y
+ /// Output X coordinate
+ /// Output Y coordinate
+ internal static void d2xy(uint d, out uint x, out uint y)
+ {
+ x = d;
+ y = (x >> 1);
+ x &= 0x55555555;
+ y &= 0x55555555;
+ x |= (x >> 1);
+ y |= (y >> 1);
+ x &= 0x33333333;
+ y &= 0x33333333;
+ x |= (x >> 2);
+ y |= (y >> 2);
+ x &= 0x0f0f0f0f;
+ y &= 0x0f0f0f0f;
+ x |= (x >> 4);
+ y |= (y >> 4);
+ x &= 0x00ff00ff;
+ y &= 0x00ff00ff;
+ x |= (x >> 8);
+ y |= (y >> 8);
+ x &= 0x0000ffff;
+ y &= 0x0000ffff;
+ }
+
+ public static CLIM analyze(string path)
+ {
+ CLIM bclim = new CLIM
+ {
+ FileName = Path.GetFileNameWithoutExtension(path),
+ FilePath = Path.GetDirectoryName(path),
+ Extension = Path.GetExtension(path)
+ };
+ byte[] byteArray = File.ReadAllBytes(path);
+ using (BinaryReader br = new BinaryReader(new MemoryStream(byteArray)))
+ {
+ br.BaseStream.Seek(br.BaseStream.Length - 0x28, SeekOrigin.Begin);
+ bclim.Magic = br.ReadUInt32();
+
+ bclim.BOM = br.ReadUInt16();
+ bclim.CLIMLength = br.ReadUInt32();
+ bclim.TileWidth = 2 << br.ReadByte();
+ bclim.TileHeight = 2 << br.ReadByte();
+ bclim.totalLength = br.ReadUInt32();
+ bclim.Count = br.ReadUInt32();
+
+ bclim.imag = br.ReadChars(4);
+ bclim.imagLength = br.ReadUInt32();
+ bclim.Width = br.ReadUInt16();
+ bclim.Height = br.ReadUInt16();
+ bclim.FileFormat = br.ReadInt32();
+ bclim.dataLength = br.ReadUInt32();
+
+ bclim.BaseSize = Math.Max(nlpo2(bclim.Width), nlpo2(bclim.Height));
+
+ br.BaseStream.Seek(0, SeekOrigin.Begin);
+ bclim.Data = br.ReadBytes((int)bclim.dataLength);
+
+ return bclim;
+ }
+ }
+ public struct CLIM
+ {
+ public UInt32 Magic; // CLIM = 0x4D494C43
+ public UInt16 BOM; // 0xFFFE
+ public UInt32 CLIMLength; // HeaderLength - 14
+ public int TileWidth; // 1<<[[n]]
+ public int TileHeight; // 1<<[[n]]
+ public UInt32 totalLength; // Total Length of file
+ public UInt32 Count; // "1" , guessing it's just Count.
+
+ public char[] imag; // imag = 0x67616D69
+ public UInt32 imagLength; // HeaderLength - 10
+ public UInt16 Width; // Final Dimensions
+ public UInt16 Height; // Final Dimensions
+ public Int32 FileFormat; // ??
+ public UInt32 dataLength; // Pixel Data Region Length
+
+ public byte[] Data;
+
+ public Int32 BaseSize;
+
+ //// Contained Data
+ //public int ColorFormat;
+ //public int ColorCount;
+ //public Color[] Colors;
+
+ //public int[] Pixels;
+
+ public string FileName;
+ public string FilePath;
+ public string Extension;
+ }
+ }
+}
\ No newline at end of file
diff --git a/pk3DS/3DS/BLZ.cs b/pk3DS/3DS/BLZ.cs
index de8c968..f050cbb 100644
--- a/pk3DS/3DS/BLZ.cs
+++ b/pk3DS/3DS/BLZ.cs
@@ -24,7 +24,7 @@
using System.IO;
using System.Windows.Forms;
-namespace BLZ
+namespace CTR
{
public class BLZCoder
{
diff --git a/pk3DS/3DS/ETC1.cs b/pk3DS/3DS/ETC1.cs
new file mode 100644
index 0000000..f269887
--- /dev/null
+++ b/pk3DS/3DS/ETC1.cs
@@ -0,0 +1,6 @@
+namespace CTR
+{
+ class ETC1
+ {
+ }
+}
diff --git a/pk3DS/3DS/GARC.cs b/pk3DS/3DS/GARC.cs
new file mode 100644
index 0000000..ed49917
--- /dev/null
+++ b/pk3DS/3DS/GARC.cs
@@ -0,0 +1,503 @@
+using System;
+using System.IO;
+using System.Linq;
+using System.Media;
+using System.Windows.Forms;
+using pk3DS;
+
+namespace CTR
+{
+ #region GARC Class & Struct
+ public class GARC
+ {
+ internal static bool garcPackMS(string folderPath, string garcPath, ProgressBar pBar1 = null, Label label = null, bool supress = false)
+ {
+ // Check to see if our input folder exists.
+ if (!new DirectoryInfo(folderPath).Exists) { Util.Error("Folder does not exist."); return false; }
+
+ // Okay some basic proofing is done. Proceed.
+ int filectr = 0;
+ // Get the paths of the files to pack up.
+ string[] files = Directory.GetFiles(folderPath);
+ string[] folders = Directory.GetDirectories(folderPath, "*.*", SearchOption.TopDirectoryOnly);
+
+ string[] packOrder = new string[files.Length + folders.Length];
+ #region Reassemble a list of filenames.
+ try
+ {
+ foreach (string f in files)
+ {
+ string fn = Path.GetFileNameWithoutExtension(f);
+ int compressed = fn.IndexOf("dec_", StringComparison.Ordinal);
+ int fileNumber = (compressed < 0)
+ ? Int32.Parse(fn)
+ : Int32.Parse(fn.Substring(compressed + 4));
+
+ packOrder[fileNumber] = f;
+ filectr++;
+ }
+ foreach (string f in folders)
+ {
+ packOrder[Int32.Parse(new DirectoryInfo(f).Name)] = f;
+ filectr += Directory.GetFiles(f).Length;
+ }
+ }
+ catch (Exception e) { Util.Error("Invalid packing filenames", e.ToString()); return false; }
+ #endregion
+
+ #region Initialize Progress Update Display
+ if (pBar1 == null) pBar1 = new ProgressBar();
+ if (pBar1.InvokeRequired)
+ pBar1.Invoke((MethodInvoker)delegate { pBar1.Minimum = 0; pBar1.Step = 1; pBar1.Value = 0; pBar1.Maximum = filectr; });
+ else { pBar1.Minimum = 0; pBar1.Step = 1; pBar1.Value = 0; pBar1.Maximum = filectr; }
+ if (label == null) label = new Label();
+ if (label.InvokeRequired)
+ label.Invoke((MethodInvoker)delegate { label.Visible = true; });
+ #endregion
+
+ // Set Up the GARC template.
+ GARCFile garc = new GARCFile
+ {
+ fato =
+ {
+ // Magic = new[] { 'O', 'T', 'A', 'F' },
+ Entries = new FATO_Entry[packOrder.Length],
+ EntryCount = (ushort)packOrder.Length,
+ HeaderSize = 0xC + packOrder.Length * 4,
+ Padding = 0xFFFF
+ },
+ fatb =
+ {
+ // Magic = new[] { 'B', 'T', 'A', 'F' },
+ Entries = new FATB_Entry[packOrder.Length],
+ FileCount = filectr
+ }
+ };
+
+ #region Start Reassembling the FAT* tables.
+ {
+ int op = 0;
+ int od = 0;
+ int v = 0;
+ int index = 0;
+ for (int i = 0; i < garc.fatb.Entries.Length; i++)
+ {
+ garc.fato.Entries[i].Offset = op; // FATO offset
+ garc.fatb.Entries[i].SubEntries = new FATB_SubEntry[32];
+ op += 4; // Vector
+ if (!Directory.Exists(packOrder[i])) // is not folder
+ {
+ garc.fatb.Entries[i].IsFolder = false;
+ garc.fatb.Entries[i].SubEntries[0].Exists = true;
+
+ string fn = Path.GetFileNameWithoutExtension(packOrder[i]);
+ int compressed = fn.IndexOf("dec_", StringComparison.Ordinal);
+ int fileNumber = (compressed < 0)
+ ? Int32.Parse(fn)
+ : Int32.Parse(fn.Substring(compressed + 4));
+
+ if (compressed >= 0)
+ {
+ string old = packOrder[i];
+ LZSS.Compress(packOrder[i], packOrder[i] = Path.Combine(Path.GetDirectoryName(packOrder[i]), fileNumber.ToString()));
+ File.Delete(old);
+ }
+
+ // Assemble Vector
+ v = 1;
+
+ // Assemble Entry
+ FileInfo fi = new FileInfo(packOrder[i]);
+ int actualLength = (int)((fi.Length % 4 == 0) ? fi.Length : fi.Length + 4 - (fi.Length % 4));
+ garc.fatb.Entries[i].SubEntries[0].Start = od;
+ garc.fatb.Entries[i].SubEntries[0].End = actualLength + garc.fatb.Entries[i].SubEntries[0].Start;
+ garc.fatb.Entries[i].SubEntries[0].Length = (int)fi.Length;
+ od += actualLength;
+
+ op += 12;
+ #region Step
+ if (pBar1.InvokeRequired)
+ pBar1.Invoke((MethodInvoker)(() => pBar1.PerformStep()));
+ else { pBar1.PerformStep(); }
+ string update = String.Format("{0:P2} - {1}/{2} - {3}", ((float)index) / ((float)filectr), index, filectr, packOrder[i]);
+ index++;
+ if (label.InvokeRequired)
+ label.Invoke((MethodInvoker)delegate { label.Text = update; });
+ else { label.Text = update; }
+ #endregion
+ }
+ else
+ {
+ garc.fatb.Entries[i].IsFolder = true;
+ string[] subFiles = Directory.GetFiles(packOrder[i]);
+ foreach (string f in subFiles)
+ {
+ string s = f;
+ string fn = Path.GetFileNameWithoutExtension(f);
+ int compressed = fn.IndexOf("dec_", StringComparison.Ordinal);
+ int fileNumber = (compressed < 0)
+ ? Int32.Parse(fn)
+ : Int32.Parse(fn.Substring(compressed + 4));
+ garc.fatb.Entries[i].SubEntries[fileNumber].Exists = true;
+
+ if (compressed >= 0)
+ {
+ LZSS.Compress(f, s = Path.Combine(Path.GetDirectoryName(f), fileNumber.ToString()));
+ File.Delete(f);
+ }
+
+ // Assemble Vector
+ v |= 1 << fileNumber;
+
+ // Assemble Entry
+ FileInfo fi = new FileInfo(s);
+ int actualLength = (int)((fi.Length % 4 == 0) ? fi.Length : fi.Length + 4 - (fi.Length % 4));
+ garc.fatb.Entries[i].SubEntries[fileNumber].Start = od;
+ garc.fatb.Entries[i].SubEntries[fileNumber].End = actualLength + garc.fatb.Entries[i].SubEntries[fileNumber].Start;
+ garc.fatb.Entries[i].SubEntries[fileNumber].Length = (int)fi.Length;
+ od += actualLength;
+
+ op += 12;
+ #region Step
+ if (pBar1.InvokeRequired)
+ pBar1.Invoke((MethodInvoker)(() => pBar1.PerformStep()));
+ else { pBar1.PerformStep(); }
+ string update = String.Format("{0:P2} - {1}/{2} - {3}", ((float)index) / ((float)filectr), index, filectr, f);
+ index++;
+ if (label.InvokeRequired)
+ label.Invoke((MethodInvoker)delegate { label.Text = update; });
+ else { label.Text = update; }
+ #endregion
+ }
+ }
+ garc.fatb.Entries[i].Vector = (uint)v;
+ }
+ garc.fatb.HeaderSize = (0xC + op);
+ }
+ #endregion
+
+ // Delete the old garc if it exists, then begin writing our new one
+ try { File.Delete(garcPath); }
+ catch { }
+
+ // Set up the Header Info
+ using (MemoryStream newGARC = new MemoryStream())
+ using (MemoryStream GARCdata = new MemoryStream())
+ using (BinaryWriter gw = new BinaryWriter(newGARC))
+ {
+ #region Write GARC Headers
+ // Write GARC
+ gw.Write((uint)0x47415243); // GARC
+ gw.Write((uint)0x0000001C); // Header Length
+ gw.Write((ushort)0xFEFF); // Endianness BOM
+ gw.Write((ushort)0x0400); // Const (4)
+ gw.Write((uint)0x00000004); // Section Count (4)
+ gw.Write((uint)0x00000000); // Data Offset (temp)
+ gw.Write((uint)0x00000000); // File Length (temp)
+ gw.Write((uint)0x00000000); // Largest File Size (temp)
+
+ // Write FATO
+ gw.Write((uint)0x4641544F); // FATO
+ gw.Write(garc.fato.HeaderSize); // Header Size
+ gw.Write(garc.fato.EntryCount); // Entry Count
+ gw.Write(garc.fato.Padding); // Padding
+ for (int i = 0; i < garc.fato.Entries.Length; i++)
+ gw.Write((uint)garc.fato.Entries[i].Offset);
+
+ // Write FATB
+ gw.Write((uint)0x46415442); // FATB
+ gw.Write(garc.fatb.HeaderSize); // Header Size
+ gw.Write(garc.fatb.FileCount); // File Count
+ foreach (var e in garc.fatb.Entries)
+ {
+ gw.Write(e.Vector);
+ foreach (var s in e.SubEntries.Where(s => s.Exists))
+ { gw.Write((uint)s.Start); gw.Write((uint)s.End); gw.Write((uint)s.Length); }
+ }
+ #endregion
+
+ #region Write Files
+ long largestSize = 0; // Required memory to allocate to handle the largest file
+ foreach (string e in packOrder)
+ {
+ string[] fa = Directory.Exists(e) ? Directory.GetFiles(e) : new[] { e };
+ foreach (string f in fa)
+ {
+ // Update largest file length if necessary
+ long len = new FileInfo(f).Length;
+ if (len > largestSize) largestSize = len;
+
+ // Write to FIMB
+ using (var input = File.OpenRead(f))
+ input.CopyTo(GARCdata);
+
+ // While length is not divisible by 4, pad with FF (unused byte)
+ while (GARCdata.Length % 4 > 0) GARCdata.WriteByte(0xFF);
+ }
+ }
+ #endregion
+
+ gw.Write((uint)0x46494D42); // FIMB
+ gw.Write((uint)0x0000000C); // Header Length
+ gw.Write((uint)GARCdata.Length); // Data Length
+
+ gw.Seek(0x10, SeekOrigin.Begin); // Goto the start of the un-set 0 data we set earlier and set it.
+ gw.Write((uint)newGARC.Length); // Write Data Offset
+ gw.Write((uint)(newGARC.Length + GARCdata.Length)); // Write total GARC Length
+ gw.Write((uint)largestSize); // Write Largest File stat (?)
+
+ newGARC.Seek(0, SeekOrigin.End); // Goto the end so we can copy the filedata after the GARC headers.
+
+ // Write in the data
+ GARCdata.Position = 0;
+ GARCdata.CopyTo(newGARC); // Copy the data.
+ // New File is ready to be saved (memstream newGARC)
+ try
+ {
+ File.WriteAllBytes(garcPath, newGARC.ToArray());
+ if (label.InvokeRequired)
+ label.Invoke((MethodInvoker)delegate { label.Visible = false; });
+ else { label.Visible = false; }
+
+ // We're done.
+ SystemSounds.Exclamation.Play();
+ if (!supress) Util.Alert("Pack Successful!", filectr + " files packed to the GARC!");
+
+ return true;
+ }
+ catch (Exception e) { Util.Error("Packing Failed!", e.ToString()); return false; }
+ }
+ }
+ internal static bool garcUnpack(string garcPath, string outPath, bool skipDecompression, ProgressBar pBar1 = null, Label label = null, bool supress = false, bool bypassExt = false)
+ {
+ if (!File.Exists(garcPath) && !supress) { Util.Alert("File does not exist"); return false; }
+
+ // Unpack the GARC
+ GARCFile garc = unpackGARC(garcPath);
+ const string ext = "bin"; // Default Extension Name
+ int fileCount = garc.fatb.FileCount;
+ string format = "D" + Math.Ceiling(Math.Log10(fileCount));
+
+ #region Display
+ // Initialize ProgressBar
+ if (pBar1 == null) pBar1 = new ProgressBar();
+ if (pBar1.InvokeRequired)
+ pBar1.Invoke((MethodInvoker)delegate { pBar1.Minimum = 0; pBar1.Step = 1; pBar1.Value = 0; pBar1.Maximum = fileCount; });
+ else { pBar1.Minimum = 0; pBar1.Step = 1; pBar1.Value = 0; pBar1.Maximum = garc.fatb.FileCount; }
+
+ if (label == null) label = new Label();
+ if (label.InvokeRequired)
+ label.Invoke((MethodInvoker)delegate { label.Visible = true; });
+ #endregion
+
+ using (BinaryReader br = new BinaryReader(File.OpenRead(garcPath)))
+ {
+ // Create Extraction folder if it does not exist.
+ if (!Directory.Exists(outPath))
+ Directory.CreateDirectory(outPath);
+
+ int filectr = 0;
+ // Pull out all the files
+ for (int o = 0; o < garc.fato.EntryCount; o++)
+ {
+
+ var Entry = garc.fatb.Entries[o];
+ // Set Entry File Name
+ string fileName = o.ToString(format);
+
+ #region OutDirectory Determination
+ string parentFolder = Entry.IsFolder ? Path.Combine(outPath, fileName) : outPath;
+ if (Entry.IsFolder) // Process Folder
+ Directory.CreateDirectory(parentFolder);
+ #endregion
+
+ uint vector = Entry.Vector;
+ for (int i = 0; i < 32; i++) // For each bit in vector
+ {
+ var SubEntry = Entry.SubEntries[i];
+ if (!SubEntry.Exists) continue;
+
+ // Seek to Offset
+ br.BaseStream.Position = SubEntry.Start + garc.DataOffset;
+
+ // Check if Compressed
+ bool compressed = false;
+ if (!skipDecompression)
+ try { compressed = (byte)br.PeekChar() == 0x11; }
+ catch { }
+
+ // Write File
+ string fileOut = Path.Combine(parentFolder, (Entry.IsFolder ? i.ToString("00") : fileName) + "." + ext);
+ using (BinaryWriter bw = new BinaryWriter(File.OpenWrite(fileOut)))
+ {
+ // Write out the data for the file
+ br.BaseStream.Position = SubEntry.Start + garc.DataOffset;
+ bw.Write(br.ReadBytes(SubEntry.Length));
+ filectr++;
+ }
+ if (compressed)
+ #region Decompression
+ {
+ string decout = Path.Combine(Path.GetDirectoryName(fileOut), "dec_" + Path.GetFileName(fileOut));
+ try
+ {
+ LZSS.Decompress(fileOut, decout);
+ try { File.Delete(fileOut); }
+ catch (Exception e) { Util.Error("A compressed file could not be deleted.", fileOut, e.ToString()); }
+ }
+ catch
+ {
+ // File is really not encrypted.
+ try { File.Delete(decout); }
+ catch (Exception e) { Util.Error("This shouldn't happen", e.ToString()); }
+ }
+ }
+ #endregion
+
+ #region Step
+ if (pBar1.InvokeRequired) pBar1.Invoke((MethodInvoker)(() => pBar1.PerformStep()));
+ else pBar1.PerformStep();
+
+ string update = String.Format("{0:P2} - {1}/{2}", (filectr) / (fileCount), filectr, fileCount);
+ if (label.InvokeRequired)
+ label.Invoke((MethodInvoker)delegate { label.Text = update; });
+ else { label.Text = update; }
+ #endregion
+
+ if ((vector >>= 1) == 0) break;
+ }
+ }
+ }
+ #region Updates
+ if (label.InvokeRequired)
+ label.Invoke((MethodInvoker)delegate { label.Visible = false; });
+ else { label.Visible = false; }
+ SystemSounds.Exclamation.Play();
+ if (!supress) Util.Alert("Unpack Successful!", fileCount + " files unpacked from the GARC!");
+ #endregion
+ return true;
+ }
+
+ internal static GARCFile unpackGARC(string path)
+ {
+ GARCFile garc = new GARCFile();
+ using (BinaryReader br = new BinaryReader(File.OpenRead(path)))
+ {
+ // GARC Header
+ garc.Magic = br.ReadChars(4);
+ garc.HeaderSize = br.ReadUInt32();
+ garc.Endianess = br.ReadUInt16();
+ garc.ChunkCount = br.ReadUInt16();
+ garc.FileSize = br.ReadUInt32();
+
+ garc.DataOffset = br.ReadUInt32();
+ garc.FileSize = br.ReadUInt32();
+ garc.LastSize = br.ReadUInt32();
+
+ // FATO (File Allocation Table Offsets)
+ garc.fato.Magic = br.ReadChars(4);
+ garc.fato.HeaderSize = br.ReadInt32();
+ garc.fato.EntryCount = br.ReadUInt16();
+ garc.fato.Padding = br.ReadUInt16();
+
+ garc.fato.Entries = new FATO_Entry[garc.fato.EntryCount];
+ for (int i = 0; i < garc.fato.EntryCount; i++)
+ garc.fato.Entries[i].Offset = br.ReadInt32();
+
+ // FATB (File Allocation Table Bits)
+ garc.fatb.Magic = br.ReadChars(4);
+ garc.fatb.HeaderSize = br.ReadInt32();
+ garc.fatb.FileCount = br.ReadInt32();
+
+ garc.fatb.Entries = new FATB_Entry[garc.fato.EntryCount];
+ for (int i = 0; i < garc.fato.EntryCount; i++) // Loop through all FATO entries
+ {
+ garc.fatb.Entries[i].Vector = br.ReadUInt32();
+ garc.fatb.Entries[i].SubEntries = new FATB_SubEntry[32];
+ uint bitvector = garc.fatb.Entries[i].Vector;
+ int ctr = 0;
+ for (int b = 0; b < 32; b++)
+ {
+ garc.fatb.Entries[i].SubEntries[b].Exists = (bitvector & 1) == 1;
+ bitvector >>= 1;
+ if (!garc.fatb.Entries[i].SubEntries[b].Exists) continue;
+ garc.fatb.Entries[i].SubEntries[b].Start = br.ReadInt32();
+ garc.fatb.Entries[i].SubEntries[b].End = br.ReadInt32();
+ garc.fatb.Entries[i].SubEntries[b].Length = br.ReadInt32();
+ ctr++;
+ }
+ garc.fatb.Entries[i].IsFolder = ctr > 1;
+ }
+
+ // FIMB (File IMage Bytes)
+ garc.fimg.Magic = br.ReadChars(4);
+ garc.fimg.HeaderSize = br.ReadInt32();
+ garc.fimg.DataSize = br.ReadInt32();
+
+ // Files data
+ // Oftentimes too large to toss into a byte array. Fetch as needed with a BinaryReader.
+ }
+ return garc;
+ }
+
+ public struct GARCFile
+ {
+ public Char[] Magic; // Always GARC = 0x4E415243
+ public UInt32 HeaderSize; // Always 0x001C
+ public UInt16 Endianess; // 0xFFFE
+ public UInt16 ChunkCount; // Always 0x0400 chunk count
+
+ public UInt32 DataOffset;
+ public UInt32 FileSize;
+ public UInt32 LastSize;
+
+ public FATO fato;
+ public FATB fatb;
+ public FIMG fimg;
+ }
+
+ public struct FATO
+ {
+ public Char[] Magic;
+ public Int32 HeaderSize;
+ public UInt16 EntryCount;
+ public UInt16 Padding;
+
+ public FATO_Entry[] Entries;
+ }
+ public struct FATO_Entry
+ {
+ public Int32 Offset;
+ }
+
+ public struct FATB
+ {
+ public Char[] Magic;
+ public Int32 HeaderSize;
+ public Int32 FileCount;
+
+ public FATB_Entry[] Entries;
+ }
+ public struct FATB_Entry
+ {
+ public UInt32 Vector;
+ public Boolean IsFolder;
+ public FATB_SubEntry[] SubEntries;
+ }
+ public struct FATB_SubEntry
+ {
+ public Boolean Exists;
+ public Int32 Start;
+ public Int32 End;
+ public Int32 Length;
+ }
+
+ public struct FIMG
+ {
+ public Char[] Magic;
+ public Int32 HeaderSize;
+ public Int32 DataSize;
+ }
+ }
+ #endregion
+}
\ No newline at end of file
diff --git a/pk3DS/GARCTool.cs b/pk3DS/3DS/LZSS.cs
similarity index 66%
rename from pk3DS/GARCTool.cs
rename to pk3DS/3DS/LZSS.cs
index 1138b61..4ed7f22 100644
--- a/pk3DS/GARCTool.cs
+++ b/pk3DS/3DS/LZSS.cs
@@ -1,397 +1,12 @@
using System;
using System.IO;
-using System.Media;
-using System.Windows.Forms;
-namespace pk3DS
+namespace CTR
{
- public class GARCTool
+ // LZSS (de)compression, heavily taken from dsdecmp
+ public class LZSS
{
- // Note: This has been customized from the original GARCTool source to not use temporary files (instead uses MemoryStream).
- public static bool garcPackMS(string folderPath, string garcPath, ProgressBar pBar1 = null, Label label = null, bool supress = false)
- {
- // Check to see if our input folder exists.
- if (!new DirectoryInfo(folderPath).Exists) { Util.Error("Folder does not exist."); return false; }
-
- // Okay some basic proofing is done. Proceed.
-
- // Get the paths of the files to pack up.
- string[] filepaths = Directory.GetFiles(folderPath, "*.*", SearchOption.TopDirectoryOnly);
-
- // Initialize ProgressBar
- if (pBar1 == null) pBar1 = new ProgressBar();
- if (label == null) label = new Label();
- if (pBar1.InvokeRequired)
- pBar1.Invoke((MethodInvoker)delegate { pBar1.Minimum = 0; pBar1.Step = 1; pBar1.Value = 0; pBar1.Maximum = filepaths.Length; });
- else { pBar1.Minimum = 0; pBar1.Step = 1; pBar1.Value = 0; pBar1.Maximum = filepaths.Length; }
- if (label.InvokeRequired)
- label.Invoke((MethodInvoker)delegate { label.Visible = true; });
- else { label.Visible = true; }
-
- // Copy the files to the working directory so our compression doesn't overwrite anything.
-
-
- // Scan through to see if we have to compress anything.
- // Delete the old garc if it exists, then write our new one
- try { File.Delete(garcPath); } catch { }
-
- // Set up the Header Info
- using (var newGARC = new MemoryStream())
- using (BinaryWriter gw = new BinaryWriter(newGARC))
- {
-
- // Write GARC header
- gw.Write((uint)0x47415243); // Write "CRAG"
- gw.Write((uint)0x0000001C); // Header Length
- gw.Write((ushort)0xFEFF); // FEFF BOM
- gw.Write((ushort)0x0400); //
- gw.Write((uint)0x00000004); //
- gw.Write((uint)0x00000000); // Data Offset
- gw.Write((uint)0x00000000); // File Length
- gw.Write((uint)0x00000000); // FATB chunk last word
-
- // Write OTAF
- gw.Write((uint)0x4641544F); // OTAF
- gw.Write((uint)(0xC + 4 * filepaths.Length)); // Section Size
- gw.Write((ushort)filepaths.Length); // Count: n
- gw.Write((ushort)0xFFFF); // padding? dunno
-
- // write BTAF jump offsets
- for (int i = 0; i < filepaths.Length; i++)
- gw.Write((uint)i * 0x10);
-
- // Start BTAF
- gw.Write((uint)0x46415442); // BTAF
- gw.Write((uint)(0xC + 0x10 * filepaths.Length)); // Chunk Size
- gw.Write((uint)filepaths.Length);
-
- uint offset = 0;
- uint largest = 0;
-
- // Sort out the files to get the actual order
- int[] fp = new int[filepaths.Length];
- try
- {
- for (int i = 0; i < fp.Length; i++)
- {
- string fn = Path.GetFileNameWithoutExtension(filepaths[i]);
- int compressed = fn.IndexOf("dec_", StringComparison.Ordinal);
- if (compressed < 0)
- fp[Int32.Parse(fn)] = i;
- else
- fp[Int32.Parse(fn.Substring(compressed + 4))] = i;
- }
- }
- catch (Exception e) { Util.Error("Invalid packing filenames", e.ToString()); return false; }
-
- // Assemble the GARCData.
- using (var GARCdata = new MemoryStream())
- {
- for (int i = 0; i < filepaths.Length; i++)
- {
- string name = filepaths[fp[i]];
- try
- {
- if (label.InvokeRequired)
- label.Invoke((MethodInvoker)delegate { label.Text = String.Format("{0:P2} - {1}/{2} - {3}", ((float)i) / ((float)filepaths.Length), i, filepaths.Length, Path.GetFileName(name)); });
- else { label.Text = String.Format("{0:P2} - {1}/{2} - {3}", ((float)i) / ((float)filepaths.Length), i, filepaths.Length, Path.GetFileName(name)); }
- }
- catch { }
- int compressed = Path.GetFileName(name).IndexOf("dec_", StringComparison.Ordinal);
- if (compressed > -1)
- {
- // File needs to be compressed and replaced.
- string compressedName = Path.Combine(Path.GetDirectoryName(name), Path.GetFileNameWithoutExtension(name).Substring(compressed + 4) + ".bin");
-
- try { dsdecmp.Compress(name, compressedName); }
- catch { throw new Exception("Compression failed"); }
-
- // Replace file name with compressed name so we can delete after packing.
- name = compressedName;
- }
- FileInfo fi = new FileInfo(name);
- using (var input = File.OpenRead(name))
- {
- gw.Write((uint)1); // garc.btaf.entries[i].bits = br.ReadUInt32();
- gw.Write((uint)offset); // Start/Begin Offset
- uint round = (uint)Math.Ceiling(((double)fi.Length / 4)) * 4;
- offset += (uint)(round); // Round up Offset.
- gw.Write((uint)offset); // End/Stop Offset
- gw.Write((uint)fi.Length); // Length/Size
-
- if (fi.Length > largest) largest = (uint)fi.Length;
-
- // Write the data to the BMIF data section
- input.CopyTo(GARCdata); // then pad with FF's if not /4
- while (GARCdata.Length % 4 > 0) GARCdata.WriteByte(0xFF);
- }
-
- // Delete file if it is compressed.
- if (compressed > -1) File.Delete(name);
-
- // Advance the ProgressBar.
- if (pBar1.InvokeRequired)
- pBar1.Invoke((MethodInvoker)delegate { pBar1.PerformStep(); });
- else { pBar1.PerformStep(); }
- }
-
- gw.Write((uint)0x46494D42);
- gw.Write((uint)0x0000000C);
- gw.Write((uint)offset);
-
- gw.Seek(0x10, SeekOrigin.Begin); // Goto the start of the un-set 0 data we set earlier and set it.
- gw.Write((uint)newGARC.Length); // Write Data Offset
- gw.Write((uint)(newGARC.Length + GARCdata.Length)); // Write total GARC Length
- gw.Write((uint)largest); // Write Largest File stat (?)
-
- newGARC.Seek(0, SeekOrigin.End); // Goto the end so we can copy the filedata after the GARC headers.
-
- // Write in the data
- GARCdata.Position = 0;
- GARCdata.CopyTo(newGARC); // Copy the data.
- }
- // New File is ready to be saved (memstream newGARC)
- try
- {
- File.WriteAllBytes(garcPath, newGARC.ToArray());
- if (label.InvokeRequired)
- label.Invoke((MethodInvoker)delegate { label.Visible = false; });
- else { label.Visible = false; }
-
- // We're done.
- SystemSounds.Exclamation.Play();
- if (!supress) Util.Alert("Pack Successful!", filepaths.Length + " files packed to the GARC!");
- return true;
- }
- catch (Exception e) { Util.Error("Packing Failed!", e.ToString()); return false; }
- }
- }
- public static bool garcUnpack(string garcPath, string outPath, bool skipDecompression, ProgressBar pBar1 = null, Label label = null, bool supress = false, bool bypassExt = false)
- {
- if (!File.Exists(garcPath) && !supress) { Util.Alert("File does not exist"); return false; }
-
- // Unpack the GARC
- GARC garc = ARC.unpackGARC(garcPath);
-
- // Initialize ProgressBar
- if (pBar1 == null) pBar1 = new ProgressBar();
- if (label == null) label = new Label();
-
-
- if (pBar1.InvokeRequired)
- pBar1.Invoke((MethodInvoker)delegate { pBar1.Minimum = 0; pBar1.Step = 1; pBar1.Value = 0; pBar1.Maximum = garc.otaf.nFiles; });
- else { pBar1.Minimum = 0; pBar1.Step = 1; pBar1.Value = 0; pBar1.Maximum = garc.otaf.nFiles; }
-
- if (label.InvokeRequired)
- label.Invoke((MethodInvoker)delegate { label.Visible = true; });
- else { label.Visible = true; }
-
- using (BinaryReader br = new BinaryReader(File.OpenRead(garcPath)))
- {
- // Create Extraction folder if it does not exist.
- if (!Directory.Exists(outPath))
- Directory.CreateDirectory(outPath);
-
- // Pull out all the files
- for (int o = 0; o < garc.otaf.nFiles; o++)
- {
- for (int i = 0; i < garc.btaf.nFiles; i++)
- {
- string ext = "bin";
- bool compressed = false;
-
- br.BaseStream.Position = garc.btaf.entries[i].start_offset + garc.data_offset;
- byte lzss = 0;
- if (!skipDecompression)
- {
- try
- { lzss = (byte)br.PeekChar(); }
- catch { lzss = 0; }
- }
- if (lzss == 0x11)
- compressed = true;
- else
- {
- ext = "bin";
- br.BaseStream.Seek(0, SeekOrigin.Begin);
- }
-
- // Set File Name
- string filename = o.ToString("D" + Math.Ceiling(Math.Log10(garc.otaf.nFiles)));
- if (garc.btaf.nFiles == garc.otaf.nFiles) filename = i.ToString("D" + Math.Ceiling(Math.Log10(garc.otaf.nFiles)));
- else if (garc.btaf.nFiles > 1 && garc.btaf.nFiles != garc.otaf.nFiles) filename += "." + i;
- string fileout = Path.Combine(outPath, filename + "." + ext);
- using (BinaryWriter bw = new BinaryWriter(File.OpenWrite(fileout)))
- {
- // Write out the data for the file
- br.BaseStream.Position = garc.btaf.entries[i].start_offset + garc.data_offset;
- for (int x = 0; x < garc.btaf.entries[i].length; x++)
- bw.Write(br.ReadByte());
- }
- // See if decompression should be attempted.
- #region Decompression
- if (compressed && !skipDecompression)
- {
- string decout = Path.Combine(outPath, "dec_" + filename + ".bin");
- try
- {
- long n = dsdecmp.Decompress(fileout, decout);
- try { File.Delete(fileout); }
- catch (Exception e) { Util.Error("A compressed file could not be deleted.", fileout, e.ToString()); }
-
- // Try to detect for extension now
- ext = "bin";
-
- File.Move(decout, Path.Combine(outPath, "dec_" + filename + "." + ext));
- }
- catch
- {
- // File is really not encrypted.
- try { File.Delete(decout); }
- catch (Exception e) { Util.Error("This shouldn't happen", e.ToString()); }
- }
- }
- if (pBar1.InvokeRequired)
- pBar1.Invoke((MethodInvoker)delegate { pBar1.PerformStep(); });
- else
- pBar1.PerformStep();
- if (label.InvokeRequired)
- label.Invoke((MethodInvoker)delegate { label.Text = String.Format("{0:P2} - {1}/{2}", ((float)i) / garc.otaf.nFiles, i, garc.otaf.nFiles); });
- else { label.Text = String.Format("{0:P2} - {1}/{2}", ((float)i) / garc.otaf.nFiles, i, garc.otaf.nFiles); }
- #endregion
- }
- if (garc.otaf.nFiles == garc.btaf.nFiles) break;
- }
- }
- if (label.InvokeRequired)
- label.Invoke((MethodInvoker)delegate { label.Visible = false; });
- else { label.Visible = false; }
- SystemSounds.Exclamation.Play();
- if (!supress) Util.Alert("Unpack Successful!", garc.otaf.nFiles + " files unpacked from the GARC!");
- return true;
- }
- }
-
- // Sub-classes
- #region GARC Class & Struct
- public partial class ARC
- {
- public static GARC unpackGARC(string path)
- {
- GARC garc = new GARC();
- BinaryReader br = new BinaryReader(File.OpenRead(path));
-
- // GARC Header
- garc.id = br.ReadChars(4);
- garc.header_size = br.ReadUInt32();
- garc.id_endian = br.ReadUInt16();
- if (garc.id_endian == 0xFEFF)
- Util.Reverse(garc.id);
- garc.constant = br.ReadUInt16();
- garc.file_size = br.ReadUInt32();
-
- garc.data_offset = br.ReadUInt32();
- garc.file_length = br.ReadUInt32();
- garc.lastsize = br.ReadUInt32();
-
- // OTAF
- garc.otaf.id = br.ReadChars(4);
- garc.otaf.section_size = br.ReadUInt32();
- garc.otaf.nFiles = br.ReadUInt16();
- garc.otaf.padding = br.ReadUInt16();
-
- garc.otaf.entries = new OTAF_Entry[garc.otaf.nFiles];
- // not really needed; plus it's wrong
- for (int i = 0; i < garc.otaf.nFiles; i++)
- {
- uint val = br.ReadUInt32();
- if (garc.otaf.padding == 0xffff)
- val = Util.Reverse(val);
-
- garc.otaf.entries[i].name = val.ToString();
- }
-
- // BTAF (File Allocation TaBle)
- garc.btaf.id = br.ReadChars(4);
- garc.btaf.section_size = br.ReadUInt32();
- garc.btaf.nFiles = br.ReadUInt32();
-
- garc.btaf.entries = new BTAF_Entry[garc.btaf.nFiles];
- garc.btaf.entries[0].bits = br.ReadUInt32();
- for (int i = 0; i < garc.btaf.nFiles; i++)
- {
- if (i != 0 && garc.btaf.nFiles == garc.otaf.nFiles)
- garc.btaf.entries[i].bits = br.ReadUInt32();
- garc.btaf.entries[i].start_offset = br.ReadUInt32();
- garc.btaf.entries[i].end_offset = br.ReadUInt32();
- garc.btaf.entries[i].length = br.ReadUInt32();
- }
-
- // BMIF
- garc.gmif.id = br.ReadChars(4);
- garc.gmif.section_size = br.ReadUInt32();
- garc.gmif.data_size = br.ReadUInt32();
-
- // Files data
-
- br.Close();
- return garc;
- }
- }
- public struct GARC
- {
- public char[] id; // Always GARC = 0x4E415243
- public UInt32 header_size; // Always 0x001C
- public UInt16 id_endian; // 0xFFFE
- public UInt16 constant; // Always 0x0400 chunk count
- public UInt32 file_size;
-
- public UInt32 data_offset;
- public UInt32 file_length;
- public UInt32 lastsize;
-
- public OTAF otaf;
- public BTAF btaf;
- public GMIF gmif;
- }
- public struct OTAF
- {
- public char[] id;
- public UInt32 section_size;
- public UInt16 nFiles;
- public UInt16 padding;
-
- public OTAF_Entry[] entries;
- }
- public struct OTAF_Entry
- {
- public string name;
- }
- public struct BTAF
- {
- public char[] id;
- public UInt32 section_size;
- public UInt32 nFiles;
- public BTAF_Entry[] entries;
- }
- public struct BTAF_Entry
- {
- public UInt32 bits;
- public UInt32 start_offset;
- public UInt32 end_offset;
- public UInt32 length;
- }
- public struct GMIF
- {
- public char[] id;
- public UInt32 section_size;
- public UInt32 data_size;
- }
- #endregion
- #region dsdecmp Classes
- public class dsdecmp // LZ11 (de)compression
- {
- public static long Decompress(string infile, string outfile)
+ internal static long Decompress(string infile, string outfile)
{
// make sure the output directory exists
string outDirectory = Path.GetDirectoryName(outfile);
@@ -419,7 +34,7 @@ public static long Decompress(string infile, string outfile)
/// The length of the output data.
/// When the given length of the input data
/// is not enough to properly decompress the input.
- public static long Decompress(Stream instream, long inLength, Stream outstream)
+ internal static long Decompress(Stream instream, long inLength, Stream outstream)
{
#region Format definition in NDSTEK style
/* Data header (32bit)
@@ -518,7 +133,7 @@ Flag data (8bit)
throw new StreamTooShortException();
int length = byte1 >> 4;
- int disp = -1;
+ int disp;
if (length == 0)
{
#region case 0; 0(B C)(D EF) + (0x11)(0x1) = (LEN)(DISP)
@@ -628,7 +243,7 @@ Flag data (8bit)
return decompressedSize;
}
- public static int Compress(string infile, string outfile)
+ internal static int Compress(string infile, string outfile)
{
// make sure the output directory exists
string outDirectory = Path.GetDirectoryName(outfile);
@@ -647,7 +262,7 @@ public static int Compress(string infile, string outfile)
/// This algorithm should yield files that are the same as those found in the games.
/// (delegates to the optimized method if LookAhead is set)
///
- public unsafe static int Compress(Stream instream, long inLength, Stream outstream, bool original)
+ internal unsafe static int Compress(Stream instream, long inLength, Stream outstream, bool original)
{
// make sure the decompressed size fits in 3 bytes.
// There should be room for four bytes, however I'm not 100% sure if that can be used
@@ -774,7 +389,7 @@ public unsafe static int Compress(Stream instream, long inLength, Stream outstre
/// and determine the optimal 'length' values for the compressed blocks. Is not 100% optimal,
/// as the flag-bytes are not taken into account.
///
- private unsafe static int CompressWithLA(Stream instream, long inLength, Stream outstream)
+ internal unsafe static int CompressWithLA(Stream instream, long inLength, Stream outstream)
{
// save the input data in an array to prevent having to go back and forth in a file
byte[] indata = new byte[inLength];
@@ -892,7 +507,7 @@ private unsafe static int CompressWithLA(Stream instream, long inLength, Stream
/// this value is the optimal 'length' value. If it is 1, the block should not be compressed.
/// The 'disp' values of the compressed blocks. May be 0, in which case the
/// corresponding length will never be anything other than 1.
- private unsafe static void GetOptimalCompressionLengths(byte* indata, int inLength, out int[] lengths, out int[] disps)
+ internal unsafe static void GetOptimalCompressionLengths(byte* indata, int inLength, out int[] lengths, out int[] disps)
{
lengths = new int[inLength];
disps = new int[inLength];
@@ -1148,5 +763,4 @@ public static int ToNDSu24(byte[] buffer, int offset)
#endregion
}
#endregion
- #endregion
-}
\ No newline at end of file
+}
diff --git a/pk3DS/3DS/mini.cs b/pk3DS/3DS/mini.cs
new file mode 100644
index 0000000..30f31a2
--- /dev/null
+++ b/pk3DS/3DS/mini.cs
@@ -0,0 +1,228 @@
+using System;
+using System.IO;
+
+namespace CTR
+{
+ // Mini Packing Util
+ class mini
+ {
+ internal static void packMini(string path, string ident, string fileName, string outExt = null, string outFolder = null, bool delete = true)
+ {
+ if (outFolder == null)
+ {
+ delete = false;
+ outFolder = path;
+ }
+ if (outExt == null) outExt = ".bin";
+ // Create new Binary with the relevant header bytes
+ byte[] data = new byte[4];
+ data[0] = (byte)ident[0];
+ data[1] = (byte)ident[1];
+ string[] files = Directory.GetFiles(path);
+ Array.Copy(BitConverter.GetBytes((ushort)files.Length), 0, data, 2, 2);
+
+ int count = files.Length;
+ int dataOffset = 4 + 4 + count * 4;
+
+ // Start the data filling.
+ using (MemoryStream dataout = new MemoryStream())
+ using (MemoryStream offsetMap = new MemoryStream())
+ using (BinaryWriter bd = new BinaryWriter(dataout))
+ using (BinaryWriter bo = new BinaryWriter(offsetMap))
+ {
+ // For each file...
+ for (int i = 0; i < count; i++)
+ {
+ // Write File Offset
+ uint fileOffset = (uint)(dataout.Position + dataOffset);
+ bo.Write(fileOffset);
+
+ // Write File to Stream
+ bd.Write(File.ReadAllBytes(files[i]));
+
+ // Pad the Data MemoryStream with Zeroes until len%4=0;
+ while (dataout.Length % 4 != 0)
+ bd.Write((byte)0);
+ // File Offset will be updated as the offset is based off of the Data length.
+
+ // Delete the File
+ File.Delete(files[i]);
+ }
+ // Cap the File
+ bo.Write((uint)(dataout.Position + dataOffset));
+
+ using (var newPack = File.Create(Path.Combine(outFolder, fileName + outExt)))
+ using (var header = new MemoryStream(data))
+ {
+ header.WriteTo(newPack);
+ offsetMap.WriteTo(newPack);
+ dataout.WriteTo(newPack);
+ }
+ }
+ if (delete)
+ Directory.Delete(path, true);
+ }
+ internal static byte[] packMini(byte[][] fileData, string ident)
+ {
+ // Create new Binary with the relevant header bytes
+ byte[] data = new byte[4];
+ data[0] = (byte)ident[0];
+ data[1] = (byte)ident[1];
+ Array.Copy(BitConverter.GetBytes((ushort)fileData.Length), 0, data, 2, 2);
+
+ int count = fileData.Length;
+ int dataOffset = 4 + 4 + count * 4;
+
+ // Start the data filling.
+ using (MemoryStream dataout = new MemoryStream())
+ using (MemoryStream offsetMap = new MemoryStream())
+ using (BinaryWriter bd = new BinaryWriter(dataout))
+ using (BinaryWriter bo = new BinaryWriter(offsetMap))
+ {
+ // For each file...
+ for (int i = 0; i < count; i++)
+ {
+ // Write File Offset
+ uint fileOffset = (uint)(dataout.Position + dataOffset);
+ bo.Write(fileOffset);
+
+ // Write File to Stream
+ bd.Write(fileData[i]);
+
+ // Pad the Data MemoryStream with Zeroes until len%4=0;
+ while (dataout.Length % 4 != 0)
+ bd.Write((byte)0);
+ // File Offset will be updated as the offset is based off of the Data length.
+ }
+ // Cap the File
+ bo.Write((uint)(dataout.Position + dataOffset));
+
+ using (var newPack = new MemoryStream())
+ using (var header = new MemoryStream(data))
+ {
+ header.WriteTo(newPack);
+ offsetMap.WriteTo(newPack);
+ dataout.WriteTo(newPack);
+ return newPack.ToArray();
+ }
+ }
+ }
+ internal static bool packMini2(string path, string ident, string fileName)
+ {
+ if (!Directory.Exists(path)) return false;
+ try
+ {
+ string[] filesToPack = Directory.GetFiles(path);
+ byte[][] fileData = new byte[filesToPack.Length][];
+ for (int i = 0; i < filesToPack.Length; i++) fileData[i] = File.ReadAllBytes(filesToPack[i]);
+ byte[] miniBytes = packMini(fileData, ident);
+ File.WriteAllBytes(fileName, miniBytes);
+ return true;
+ }
+ catch { return false; }
+ }
+ internal static void unpackMini(string path, string ident, string outFolder = null, bool delete = true)
+ {
+ if (outFolder == null) outFolder = Path.GetDirectoryName(path);
+ if (!Directory.Exists(outFolder)) Directory.CreateDirectory(outFolder);
+ using (var s = new MemoryStream(File.ReadAllBytes(path)))
+ using (var br = new BinaryReader(s))
+ {
+ string fx = new string(br.ReadChars(2));
+
+ if (fx != ident) return;
+
+ ushort count = br.ReadUInt16();
+ string namePad = "D" + Math.Ceiling(Math.Log10(count));
+ uint[] offsets = new uint[count + 1];
+ for (int i = 0; i < count; i++)
+ offsets[i] = br.ReadUInt32();
+
+ uint length = br.ReadUInt32();
+ offsets[offsets.Length - 1] = length;
+
+ for (int i = 0; i < count; i++)
+ {
+ br.BaseStream.Seek(offsets[i], SeekOrigin.Begin);
+ using (MemoryStream dataout = new MemoryStream())
+ {
+ byte[] data = new byte[0];
+ s.CopyTo(dataout, (int)offsets[i]);
+ int len = (int)offsets[i + 1] - (int)offsets[i];
+
+ if (len != 0)
+ {
+ data = dataout.ToArray();
+ Array.Resize(ref data, len);
+ }
+ string newFile = Path.Combine(outFolder, i.ToString(namePad) + ".bin");
+ File.WriteAllBytes(newFile, data);
+ }
+ }
+ }
+ if (delete)
+ File.Delete(path); // File is unpacked.
+ }
+ internal static byte[][] unpackMini(byte[] fileData, string ident)
+ {
+ using (var s = new MemoryStream(fileData))
+ using (var br = new BinaryReader(s))
+ {
+ string fx = new string(br.ReadChars(2));
+
+ if (fx != ident) return null;
+
+ ushort count = br.ReadUInt16();
+ byte[][] returnData = new byte[count][];
+
+ uint[] offsets = new uint[count + 1];
+ for (int i = 0; i < count; i++)
+ offsets[i] = br.ReadUInt32();
+
+ uint length = br.ReadUInt32();
+ offsets[offsets.Length - 1] = length;
+
+ for (int i = 0; i < count; i++)
+ {
+ br.BaseStream.Seek(offsets[i], SeekOrigin.Begin);
+ using (MemoryStream dataout = new MemoryStream())
+ {
+ byte[] data = new byte[0];
+ s.CopyTo(dataout, (int)offsets[i]);
+ int len = (int)offsets[i + 1] - (int)offsets[i];
+
+ if (len != 0)
+ {
+ data = dataout.ToArray();
+ Array.Resize(ref data, len);
+ }
+ returnData[i] = data;
+ }
+ }
+ return returnData;
+ }
+ }
+ internal static string getIsMini(string path)
+ {
+ byte[] data = File.ReadAllBytes(path);
+ var fi = new FileInfo(path);
+ try
+ {
+ string fx = new string(new[] { (char)data[0], (char)data[1] });
+ ushort count = BitConverter.ToUInt16(data, 2);
+
+ uint[] offsets = new uint[count + 1];
+ uint length = 1338;
+ for (int i = 0; i < count; i++)
+ {
+ offsets[i] = BitConverter.ToUInt32(data, 4 + i * 4);
+ length = BitConverter.ToUInt32(data, 8 + i * 4);
+ }
+
+ offsets[offsets.Length - 1] = length;
+ return (fi.Length == length) ? fx : null;
+ }
+ catch { return null; }
+ }
+ }
+}
diff --git a/pk3DS/ARCUtil.cs b/pk3DS/ARCUtil.cs
index 2bddd12..93e9f19 100644
--- a/pk3DS/ARCUtil.cs
+++ b/pk3DS/ARCUtil.cs
@@ -6,7 +6,7 @@
namespace pk3DS
{
- public partial class ARC
+ public class ARC
{
// Multi Type Archive Handling
internal static Boolean onefile = true;
@@ -468,7 +468,6 @@ internal static string Interpret(string path)
{
var fs = File.OpenRead(path);
uint FileLen = t.FileDataEnd - t.FileDataStart;
- string FileName = "";
fs.Seek(t.FileDataStart + sarc.DataOffset, SeekOrigin.Begin);
byte[] fileBuffer = new byte[FileLen];
fs.Read(fileBuffer, 0, (int)FileLen);
@@ -479,7 +478,7 @@ internal static string Interpret(string path)
{
sb.Append(c);
}
- FileName = sb.ToString().Replace('/', Path.DirectorySeparatorChar);
+ string FileName = sb.ToString().Replace('/', Path.DirectorySeparatorChar);
fs.Close();
string FileDir = Path.GetDirectoryName(dir + FileName) + Path.DirectorySeparatorChar;
if (!Directory.Exists(FileDir))
@@ -496,213 +495,7 @@ internal static string Interpret(string path)
}
return ret;
}
-
- // Mini Packing Util
- internal static void packMini(string path, string ident, string fileName, string outExt = null, string outFolder = null, bool delete = true)
- {
- if (outFolder == null)
- {
- delete = false;
- outFolder = path;
- }
- if (outExt == null) outExt = ".bin";
- // Create new Binary with the relevant header bytes
- byte[] data = new byte[4];
- data[0] = (byte)ident[0];
- data[1] = (byte)ident[1];
- string[] files = Directory.GetFiles(path);
- Array.Copy(BitConverter.GetBytes((ushort)files.Length), 0, data, 2, 2);
-
- int count = files.Length;
- int dataOffset = 4 + 4 + count * 4;
-
- // Start the data filling.
- using (MemoryStream dataout = new MemoryStream())
- using (MemoryStream offsetMap = new MemoryStream())
- using (BinaryWriter bd = new BinaryWriter(dataout))
- using (BinaryWriter bo = new BinaryWriter(offsetMap))
- {
- // For each file...
- for (int i = 0; i < count; i++)
- {
- // Write File Offset
- uint fileOffset = (uint)(dataout.Position + dataOffset);
- bo.Write(fileOffset);
-
- // Write File to Stream
- bd.Write(File.ReadAllBytes(files[i]));
-
- // Pad the Data MemoryStream with Zeroes until len%4=0;
- while (dataout.Length % 4 != 0)
- bd.Write((byte)0);
- // File Offset will be updated as the offset is based off of the Data length.
-
- // Delete the File
- File.Delete(files[i]);
- }
- // Cap the File
- bo.Write((uint)(dataout.Position + dataOffset));
-
- using (var newPack = File.Create(Path.Combine(outFolder, fileName + outExt)))
- using (var header = new MemoryStream(data))
- {
- header.WriteTo(newPack);
- offsetMap.WriteTo(newPack);
- dataout.WriteTo(newPack);
- }
- }
- if (delete)
- Directory.Delete(path, true);
- }
- internal static byte[] packMini(byte[][] fileData, string ident)
- {
- // Create new Binary with the relevant header bytes
- byte[] data = new byte[4];
- data[0] = (byte)ident[0];
- data[1] = (byte)ident[1];
- Array.Copy(BitConverter.GetBytes((ushort)fileData.Length), 0, data, 2, 2);
-
- int count = fileData.Length;
- int dataOffset = 4 + 4 + count * 4;
-
- // Start the data filling.
- using (MemoryStream dataout = new MemoryStream())
- using (MemoryStream offsetMap = new MemoryStream())
- using (BinaryWriter bd = new BinaryWriter(dataout))
- using (BinaryWriter bo = new BinaryWriter(offsetMap))
- {
- // For each file...
- for (int i = 0; i < count; i++)
- {
- // Write File Offset
- uint fileOffset = (uint)(dataout.Position + dataOffset);
- bo.Write(fileOffset);
-
- // Write File to Stream
- bd.Write(fileData[i]);
-
- // Pad the Data MemoryStream with Zeroes until len%4=0;
- while (dataout.Length % 4 != 0)
- bd.Write((byte)0);
- // File Offset will be updated as the offset is based off of the Data length.
- }
- // Cap the File
- bo.Write((uint)(dataout.Position + dataOffset));
-
- using (var newPack = new MemoryStream())
- using (var header = new MemoryStream(data))
- {
- header.WriteTo(newPack);
- offsetMap.WriteTo(newPack);
- dataout.WriteTo(newPack);
- return newPack.ToArray();
- }
- }
- }
- internal static void unpackMini(string path, string ident, string outFolder = null, bool delete = true)
- {
- if (outFolder == null) outFolder = Path.GetDirectoryName(path);
- if (!Directory.Exists(outFolder)) Directory.CreateDirectory(outFolder);
- using (var s = new MemoryStream(File.ReadAllBytes(path)))
- using (var br = new BinaryReader(s))
- {
- string fx = new string(br.ReadChars(2));
-
- if (fx != ident) return;
-
- ushort count = br.ReadUInt16();
- string namePad = "D" + Math.Ceiling(Math.Log10(count));
- uint[] offsets = new uint[count + 1];
- for (int i = 0; i < count; i++)
- offsets[i] = br.ReadUInt32();
-
- uint length = br.ReadUInt32();
- offsets[offsets.Length - 1] = length;
-
- for (int i = 0; i < count; i++)
- {
- br.BaseStream.Seek(offsets[i], SeekOrigin.Begin);
- using (MemoryStream dataout = new MemoryStream())
- {
- byte[] data = new byte[0];
- s.CopyTo(dataout, (int)offsets[i]);
- int len = (int)offsets[i + 1] - (int)offsets[i];
-
- if (len != 0)
- {
- data = dataout.ToArray();
- Array.Resize(ref data, len);
- }
- string newFile = Path.Combine(outFolder, i.ToString(namePad) + ".bin");
- File.WriteAllBytes(newFile, data);
- }
- }
- }
- if (delete)
- File.Delete(path); // File is unpacked.
- }
- internal static byte[][] unpackMini(byte[] fileData, string ident)
- {
- using (var s = new MemoryStream(fileData))
- using (var br = new BinaryReader(s))
- {
- string fx = new string(br.ReadChars(2));
-
- if (fx != ident) return null;
-
- ushort count = br.ReadUInt16();
- byte[][] returnData = new byte[count][];
-
- uint[] offsets = new uint[count + 1];
- for (int i = 0; i < count; i++)
- offsets[i] = br.ReadUInt32();
-
- uint length = br.ReadUInt32();
- offsets[offsets.Length - 1] = length;
-
- for (int i = 0; i < count; i++)
- {
- br.BaseStream.Seek(offsets[i], SeekOrigin.Begin);
- using (MemoryStream dataout = new MemoryStream())
- {
- byte[] data = new byte[0];
- s.CopyTo(dataout, (int)offsets[i]);
- int len = (int)offsets[i + 1] - (int)offsets[i];
-
- if (len != 0)
- {
- data = dataout.ToArray();
- Array.Resize(ref data, len);
- }
- returnData[i] = data;
- }
- }
- return returnData;
- }
- }
- internal static string getIsMini(string path)
- {
- byte[] data = File.ReadAllBytes(path);
- var fi = new FileInfo(path);
- try
- {
- string fx = new string(new[]{(char)data[0], (char)data[1]});
- ushort count = BitConverter.ToUInt16(data, 2);
-
- uint[] offsets = new uint[count + 1];
- uint length = 1338;
- for (int i = 0; i < count; i++)
- {
- offsets[i] = BitConverter.ToUInt32(data, 4 + i*4);
- length = BitConverter.ToUInt32(data, 8 + i*4);
- }
-
- offsets[offsets.Length - 1] = length;
- return (fi.Length == length) ? fx : null;
- }
- catch { return null; }
- }
-
+
// Unpacking
internal static string unpackDARC(string path, string outFolder = null, bool delete = true)
{
diff --git a/pk3DS/Main.cs b/pk3DS/Main.cs
index 5694d81..b1ec376 100644
--- a/pk3DS/Main.cs
+++ b/pk3DS/Main.cs
@@ -20,8 +20,6 @@
using System.Media;
using System.Threading;
using System.Windows.Forms;
-using BLZ;
-using CTR;
namespace pk3DS
{
@@ -165,14 +163,14 @@ private void openQuick(string path)
if (fi.Name.Contains("code.bin")) // Compress/Decompress .code.bin
{
if (fi.Length % 0x200 == 0 && (Util.Prompt(MessageBoxButtons.YesNo, "Detected Decompressed code.bin.", "Compress? File will be replaced.") == DialogResult.Yes))
- new Thread(() => { threads++; new BLZCoder(new[] { "-en", path }, pBar1); threads--; Util.Alert("Compressed!"); }).Start();
+ new Thread(() => { threads++; new CTR.BLZCoder(new[] { "-en", path }, pBar1); threads--; Util.Alert("Compressed!"); }).Start();
else if (Util.Prompt(MessageBoxButtons.YesNo, "Detected Compressed code.bin.", "Decompress? File will be replaced.") == DialogResult.Yes)
- new Thread(() => { threads++; new BLZCoder(new[] { "-d", path }, pBar1); threads--; Util.Alert("Decompressed!"); }).Start();
+ new Thread(() => { threads++; new CTR.BLZCoder(new[] { "-d", path }, pBar1); threads--; Util.Alert("Decompressed!"); }).Start();
}
else if (fi.Name.ToLower().Contains("exe")) // Unpack exefs
{
if (fi.Length % 0x200 == 0 && (Util.Prompt(MessageBoxButtons.YesNo, "Detected ExeFS.bin.", "Unpack?") == DialogResult.Yes))
- new Thread(() => { threads++; ExeFS.get(path, Path.GetDirectoryName(path)); threads--; Util.Alert("Unpacked!"); }).Start();
+ new Thread(() => { threads++; CTR.ExeFS.get(path, Path.GetDirectoryName(path)); threads--; Util.Alert("Unpacked!"); }).Start();
}
else if (fi.Name.ToLower().Contains("rom"))
{
@@ -275,7 +273,7 @@ private bool checkIfExeFS(string path)
FileInfo fi = new FileInfo(files[0]);
if (!fi.Name.Contains("code")) return false;
if (fi.Length % 0x200 != 0 && (Util.Prompt(MessageBoxButtons.YesNo, "Detected Compressed code.bin.", "Decompress? File will be replaced.") == DialogResult.Yes))
- new Thread(() => { threads++; new BLZCoder(new[] { "-d", files[0] }, pBar1); threads--; Util.Alert("Decompressed!"); }).Start();
+ new Thread(() => { threads++; new CTR.BLZCoder(new[] { "-d", files[0] }, pBar1); threads--; Util.Alert("Decompressed!"); }).Start();
ExeFSPath = path;
return true;
@@ -533,7 +531,7 @@ private void rebuildExeFS(object sender, EventArgs e)
new Thread(() =>
{
threads++;
- new BLZCoder(new[] { "-en", files[file] }, pBar1);
+ new CTR.BLZCoder(new[] { "-en", files[file] }, pBar1);
Util.Alert("Compressed!");
CTR.ExeFS.set(Directory.GetFiles(ExeFSPath), sfd.FileName);
threads--;
@@ -647,7 +645,7 @@ public bool getGARC(string infile, string outfolder, bool PB, bool bypassExt = f
}
try
{
- bool success = GARCTool.garcUnpack(infile, outfolder, bypassExt, (PB) ? pBar1 : null, null, true, bypassExt);
+ bool success = CTR.GARC.garcUnpack(infile, outfolder, bypassExt, (PB) ? pBar1 : null, null, true, bypassExt);
updateStatus(String.Format(success ? "Success!" : "Failed!"), false);
threads--;
return success;
@@ -661,7 +659,7 @@ public bool setGARC(string outfile, string infolder, bool PB)
try
{
- bool success = GARCTool.garcPackMS(infolder, outfile, (PB) ? pBar1 : null, null, true);
+ bool success = CTR.GARC.garcPackMS(infolder, outfile, (PB) ? pBar1 : null, null, true);
threads--;
updateStatus(String.Format(success ? "Success!" : "Failed!"), false);
return success;
diff --git a/pk3DS/Subforms/Moves.cs b/pk3DS/Subforms/Moves.cs
index 27f3a5a..947876b 100644
--- a/pk3DS/Subforms/Moves.cs
+++ b/pk3DS/Subforms/Moves.cs
@@ -9,7 +9,7 @@ public partial class Moves : Form
public Moves()
{
if (Main.oras)
- ARC.unpackMini(Directory.GetFiles("move")[0], "WD");
+ CTR.mini.unpackMini(Directory.GetFiles("move")[0], "WD");
movelist[0] = "";
sortedmoves = (string[])movelist.Clone();
@@ -165,7 +165,7 @@ private void formClosing(object sender, FormClosingEventArgs e)
setEntry();
if (Main.oras)
- ARC.packMini("move", "WD", "0", ".bin");
+ CTR.mini.packMini("move", "WD", "0", ".bin");
}
private void B_RandAll_Click(object sender, EventArgs e)
@@ -190,14 +190,14 @@ private void B_RandAll_Click(object sender, EventArgs e)
internal static int[] getTypes()
{
if (Main.oras)
- ARC.unpackMini(Directory.GetFiles("move")[0], "WD");
+ CTR.mini.unpackMini(Directory.GetFiles("move")[0], "WD");
string[] f2 = Directory.GetFiles("move");
int[] moveTypes = new int[f2.Length];
for (int i = 0; i < f2.Length; i++)
moveTypes[i] = File.ReadAllBytes(f2[i])[0];
if (Main.oras)
- ARC.packMini("move", "WD", "0", ".bin");
+ CTR.mini.packMini("move", "WD", "0", ".bin");
return moveTypes;
}
diff --git a/pk3DS/Subforms/OWSE.cs b/pk3DS/Subforms/OWSE.cs
index a82e89e..8e471fe 100644
--- a/pk3DS/Subforms/OWSE.cs
+++ b/pk3DS/Subforms/OWSE.cs
@@ -74,7 +74,7 @@ private void getEntry()
if (entry < 0) return;
RTB_F.Text = RTB_O.Text = RTB_W.Text = RTB_T.Text = string.Empty;
byte[] raw = File.ReadAllBytes(filepaths[entry]);
- locationData = ARC.unpackMini(raw, "ZO");
+ locationData = CTR.mini.unpackMini(raw, "ZO");
if (locationData == null) return;
RichTextBox[] rtba = {RTB_MapInfo, RTB_OWSC, RTB_MapSC, RTB_Encounter, RTB_File5};
diff --git a/pk3DS/Tools/ToolsUI.Designer.cs b/pk3DS/Tools/ToolsUI.Designer.cs
index c1188a2..9a5a6f5 100644
--- a/pk3DS/Tools/ToolsUI.Designer.cs
+++ b/pk3DS/Tools/ToolsUI.Designer.cs
@@ -103,7 +103,6 @@ private void InitializeComponent()
this.CB_Repack.Name = "CB_Repack";
this.CB_Repack.Size = new System.Drawing.Size(150, 21);
this.CB_Repack.TabIndex = 8;
- this.CB_Repack.SelectedIndexChanged += new System.EventHandler(this.CB_Repack_SelectedIndexChanged);
//
// PB_BCLIM
//
diff --git a/pk3DS/Tools/ToolsUI.cs b/pk3DS/Tools/ToolsUI.cs
index 16feb06..733a0b2 100644
--- a/pk3DS/Tools/ToolsUI.cs
+++ b/pk3DS/Tools/ToolsUI.cs
@@ -20,7 +20,6 @@ public ToolsUI()
PB_BCLIM.DragDrop += tabMain_DragDrop;
CLIMWindow = PB_BCLIM.Size;
CB_Repack.Items.Add("GARC Pack");
- CB_Repack.Items.Add("GARC Pack (from Existing)");
CB_Repack.Items.Add("DARC Pack (use filenames)");
CB_Repack.Items.Add("Mini Pack (from Name)");
CB_Repack.SelectedIndex = 2;
@@ -51,7 +50,7 @@ private void dropLeave(object sender, EventArgs e)
}
private void openIMG(string path)
{
- var img = png2bclim.makeBMP(path, CHK_PNG.Checked);
+ var img = CTR.BCLIM.makeBMP(path, CHK_PNG.Checked);
if (img == null) return;
PB_BCLIM.Size = new Size(img.Width + 2, img.Height + 2);
PB_BCLIM.BackgroundImage = img;
@@ -67,12 +66,12 @@ private void openARC(string path, bool recursing = false)
// Determine if it is a DARC or a Mini
// Check if Mini first
- string fx = ARC.getIsMini(path);
+ string fx = CTR.mini.getIsMini(path);
string newFolder = folderPath + "_" + fx;
if (fx != null) // Is Mini Packed File
{
// Fetch Mini File Contents
- ARC.unpackMini(path, fx, newFolder, false);
+ CTR.mini.unpackMini(path, fx, newFolder, false);
// Recurse throught the extracted contents if they extract successfully
if (Directory.Exists(newFolder))
foreach (string file in Directory.GetFiles(newFolder))
@@ -88,11 +87,13 @@ private void openARC(string path, bool recursing = false)
if (pos >= data.Length) return;
}
var darcData = data.Skip(pos).ToArray();
- var res = CTR.DARC.darc2files(darcData, folderPath + "_d");
+ bool r = CTR.DARC.darc2files(darcData, folderPath + "_d");
+ if (!r) Util.Alert("Unpacking failed.");
}
else if (BitConverter.ToUInt32(File.ReadAllBytes(path), 0) == 0x47415243) // GARC
{
- GARCTool.garcUnpack(path, folderPath + "_g", false);
+ bool r = CTR.GARC.garcUnpack(path, folderPath + "_g", false);
+ if (!r) Util.Alert("Unpacking failed.");
}
else if (!recursing)
{ Util.Alert("File is not a darc or a mini packed file:" + Environment.NewLine + path); return ;}
@@ -114,10 +115,13 @@ private void saveARC(string path)
int type = CB_Repack.SelectedIndex;
switch (type)
{
- case -1: return;
case 0: // GARC Pack
- case 1: // GARC Pack Existing
- case 2: // DARC Pack
+ {
+ bool r = CTR.GARC.garcPackMS(path, folderName + ".garc");
+ if (!r) Util.Alert("Packing failed.");
+ break;
+ }
+ case 1: // DARC Pack (from existing if exists)
{
string oldFile = path.Replace("_d", "");
if (File.Exists(Path.Combine(parentName, oldFile)))
@@ -127,10 +131,12 @@ private void saveARC(string path)
else if (File.Exists(Path.Combine(parentName, oldFile + ".darc")))
oldFile = Path.Combine(parentName, oldFile + ".darc");
else oldFile = null;
- bool result = CTR.DARC.files2darc(path, false, oldFile);
+
+ bool r = CTR.DARC.files2darc(path, false, oldFile);
+ if (!r) Util.Alert("Packing failed.");
break;
}
- case 3: // Mini Pack
+ case 2: // Mini Pack
{
string fileName = folderName.Replace("_", "");
if (fileName.Length < 2) { Util.Error("Mini Folder name not valid:", path); return; }
@@ -138,15 +144,8 @@ private void saveARC(string path)
string fileExt = fileName.Substring(fileName.Length - 2);
string fileNum = fileName.Substring(0, fileName.Length - 3); // ignore "."
- string[] filesToPack = Directory.GetFiles(path);
- byte[][] data = new byte[filesToPack.Length][];
- for (int i = 0; i < filesToPack.Length; i++) data[i] = File.ReadAllBytes(filesToPack[i]);
- byte[] miniBytes = ARC.packMini(data, fileExt);
-
- string newName = fileNum + "." + fileExt;
- File.WriteAllBytes(parentName + newName, miniBytes);
- Util.Alert("Mini Folder Repacked!",
- String.Format("InFolder: {0}{2}OutFile{1}", folderName, newName, Path.DirectorySeparatorChar));
+ bool r = CTR.mini.packMini2(path, fileExt, parentName + fileNum + "." + fileExt);
+ if (!r) Util.Alert("Packing failed.");
break;
}
default: Util.Alert("Repacking not implemented." + Environment.NewLine + path);
@@ -171,14 +170,5 @@ private void B_Reset_Click(object sender, EventArgs e)
{
PB_BCLIM.Size = CLIMWindow;
}
-
- private void CB_Repack_SelectedIndexChanged(object sender, EventArgs e)
- {
- if (CB_Repack.SelectedIndex < 2) // 0,1
- {
- Util.Alert("Not implemented:", CB_Repack.Text);
- CB_Repack.SelectedIndex = 2;
- }
- }
}
}
\ No newline at end of file
diff --git a/pk3DS/pk3DS.csproj b/pk3DS/pk3DS.csproj
index 49e59c5..9ca3c09 100644
--- a/pk3DS/pk3DS.csproj
+++ b/pk3DS/pk3DS.csproj
@@ -52,14 +52,17 @@
+
+
+
-
+
@@ -146,7 +149,7 @@
EggMove.cs
-
+
Form
diff --git a/pk3DS/png2bclim.cs b/pk3DS/png2bclim.cs
deleted file mode 100644
index e3fe769..0000000
--- a/pk3DS/png2bclim.cs
+++ /dev/null
@@ -1,1537 +0,0 @@
-using System;
-using System.Drawing;
-using System.Drawing.Imaging;
-using System.IO;
-using System.Runtime.InteropServices;
-using System.Windows.Forms;
-
-namespace pk3DS
-{
- class png2bclim
- {
- internal static void openFile(string path, bool autosave = false, bool crop = true, char format = 'X')
- {
- // Handle file
- if (!File.Exists(path)) throw new Exception("Can only accept files, not folders");
- string ext = Path.GetExtension(path);
- if (ext == ".png")
- makeBCLIM(path, format);
- else if (ext == ".bin" || ext == ".bclim")
- makeBMP(path, autosave, crop);
- }
-
- internal static Image makeBCLIM(string path, char fc)
- {
- byte[] byteArray = File.ReadAllBytes(path);
- using (Stream BitmapStream = new MemoryStream(byteArray)) // Open the file, even if it is in use.
- {
- Image img = Image.FromStream(BitmapStream);
- Bitmap mBitmap = new Bitmap(img);
- // Pixel format acquired. Now to determine how we want to save the data.
- MemoryStream ms = new MemoryStream();
- int bclimformat = 7; // Init to default (for X)
-
- if (fc == 'X')
- write16BitColorPalette(mBitmap, ref ms);
- else
- {
- bclimformat = Convert.ToInt16(fc.ToString(), 16);
- try
- {
- writeGeneric(bclimformat, mBitmap, ref ms);
- }
- catch (Exception e)
- {
- System.Media.SystemSounds.Beep.Play();
- System.Diagnostics.Debug.WriteLine(e.ToString());
- }
- }
-
- long datalength = ms.Length;
- // Write the CLIM + imag data.
- using (BinaryWriter bw = new BinaryWriter(ms))
- {
- bw.Write((uint)0x4D494C43); // CLIM
- bw.Write((ushort)0xFEFF); // BOM
- bw.Write((uint)0x14);
- bw.Write((ushort)0x0202); // 2 2
- bw.Write((uint)(datalength+0x28));
- bw.Write((uint)1);
- bw.Write((uint)0x67616D69);
- bw.Write((uint)0x10);
- bw.Write((ushort)mBitmap.Width);
- bw.Write((ushort)mBitmap.Height);
- bw.Write((uint)bclimformat);
- bw.Write((uint)datalength);
- }
- string fp = Path.GetFileNameWithoutExtension(path);
- fp = "new_" + fp.Substring(fp.IndexOf('_') + 1);
- string pp = Path.GetDirectoryName(path);
- string newPath = Path.Combine(pp, fp + ".bclim");
- File.WriteAllBytes(newPath, ms.ToArray());
-
- return makeBMP(newPath);
- }
- }
- internal static Image makeBMP(string path, bool autosave = false, bool crop = true)
- {
- CLIM bclim = BCLIM.analyze(path);
- if (bclim.Magic != 0x4D494C43)
- {
- System.Media.SystemSounds.Beep.Play();
- return null;
- }
-
- // Interpret data.
- int f = bclim.FileFormat;
- if (f > 13)
- {
- System.Media.SystemSounds.Exclamation.Play();
- return null;
- }
-
- Bitmap img;
- if (f == 7 && BitConverter.ToUInt16(bclim.Data, 0) == 2)
- // PKM XY Format 7 (Color Palette)
- img = getIMG_XY7(bclim);
- else if (f == 10 || f == 11)
- img = getIMG_ETC(bclim);
- else
- img = getIMG(bclim);
-
- Rectangle cropRect = new Rectangle(0, 0, bclim.Width, bclim.Height);
- Bitmap CropBMP = new Bitmap(cropRect.Width, cropRect.Height);
- using (Graphics g = Graphics.FromImage(CropBMP))
- {
- g.DrawImage(img,
- new Rectangle(0, 0, CropBMP.Width, CropBMP.Height),
- cropRect,
- GraphicsUnit.Pixel);
- }
- if (!autosave) return !crop ? img : CropBMP;
-
- using (MemoryStream ms = new MemoryStream())
- {
- //error will throw from here
- CropBMP.Save(ms, ImageFormat.Png);
- byte[] data = ms.ToArray();
- File.WriteAllBytes(bclim.FilePath + "\\" + bclim.FileName + ".png", data);
- }
- return !crop ? img : CropBMP;
- }
- // Bitmap Data Writing
- internal static Bitmap getIMG(CLIM bclim)
- {
- // New Image
- Bitmap img = new Bitmap(nlpo2(gcm(bclim.Width,8)), nlpo2(gcm(bclim.Height,8)));
- int f = bclim.FileFormat;
- int area = img.Width * img.Height;
- if (f == 9 && area > bclim.Data.Length/4)
- {
- img = new Bitmap(gcm(bclim.Width, 8), gcm(bclim.Height, 8));
- area = img.Width * img.Height;
- }
- // Coordinates
- // Colors
- // Tiles Per Width
- int p = gcm(img.Width, 8) / 8;
- if (p == 0) p = 1;
-
- // Build Image
- using (Stream BitmapStream = new MemoryStream(bclim.Data))
- using (BinaryReader br = new BinaryReader(BitmapStream))
- for (uint i = 0; i < area; i++) // for every pixel
- {
- uint x;
- uint y;
- d2xy(i % 64, out x, out y);
- uint tile = i / 64;
-
- // Shift Tile Coordinate into Tilemap
- x += (uint)(tile % p) * 8;
- y += (uint)(tile / p) * 8;
-
- // Get Color
- Color c;
- switch (f)
- {
- case 0x0: // L8 // 8bit/1 byte
- case 0x1: // A8
- case 0x2: // LA4
- c = DecodeColor(br.ReadByte(), f);
- break;
- case 0x3: // LA8 // 16bit/2 byte
- case 0x4: // HILO8
- case 0x5: // RGB565
- case 0x8: // RGBA4444
- case 0x7: // RGBA5551
- c = DecodeColor(br.ReadUInt16(), f);
- break;
- case 0x6: // RGB8: // 24bit
- byte[] data = br.ReadBytes(3); Array.Resize(ref data, 4);
- c = DecodeColor(BitConverter.ToUInt32(data, 0), f);
- break;
- case 0x9: // RGBA8888
- c = DecodeColor(br.ReadUInt32(), f);
- break;
- case 0xC: // L4
- case 0xD: // A4 // 4bit - Do 2 pixels at a time.
- uint val = br.ReadByte();
- img.SetPixel((int)x, (int)y, DecodeColor(val & 0xF, f)); // lowest bits for the low pixel
- i++; x++;
- c = DecodeColor(val >> 4, f); // highest bits for the high pixel
- break;
- default: throw new Exception("Invalid FileFormat.");
- }
- img.SetPixel((int)x, (int)y, c);
- }
- return img;
- }
- internal static Bitmap getIMG_XY7(CLIM bclim)
- {
- Bitmap img = new Bitmap(bclim.BaseSize, bclim.BaseSize);
- using (Stream BitmapStream = new MemoryStream(bclim.Data))
- using (BinaryReader br = new BinaryReader(BitmapStream))
- {
- // Fetch Color stuff.
- if (br.ReadUInt16() != 2) return null;
- ushort colors = br.ReadUInt16();
- Color[] ca = new Color[colors];
- for (int i = 0; i < colors; i++)
- ca[i] = DecodeColor(br.ReadUInt16(), 7);
-
- // Coordinates
- // Colors
- // Tiles Per Width
- int p = gcm(img.Width, 8) / 8;
- if (p == 0) p = 1;
-
- for (uint i = 0; i < bclim.BaseSize * bclim.BaseSize; i++) // for every pixel
- {
- uint x;
- uint y;
- d2xy(i % 64, out x, out y);
- uint tile = i / 64;
-
- // Shift Tile Coordinate into Tilemap
- x += (uint)(tile % p) * 8;
- y += (uint)(tile / p) * 8;
-
- byte val = br.ReadByte();
- if (colors <= 0x10) // Handle 2 pixels at a time
- {
- img.SetPixel((int)x, (int)y, ca[val >> 4]);
- x++; i++; val &= 0xF;
- img.SetPixel((int)x, (int)y, ca[val]);
- }
- else //1bpp instead of .5, handle 2 pixels at a time the same way for no reason
- {
- img.SetPixel((int)x, (int)y, ca[val]);
- x++; i++; val = br.ReadByte();
- img.SetPixel((int)x, (int)y, ca[val]);
- }
- }
- }
- return img;
- }
- internal static Bitmap getIMG_ETC(CLIM bclim)
- {
- int format = bclim.FileFormat;
- if (format != 10 && format != 11)
- return null;
-
- byte[] data = ETC1_Decompress(bclim.Data, bclim.Width, bclim.Height, format + 2);
- Bitmap img = new Bitmap(bclim.Width, bclim.Height);
-
- for (int i = 0; i < bclim.Width * bclim.Height; i++) // for every pixel
- {
- Color c = DecodeColor(BitConverter.ToUInt32(data, i * 4), 9);
- int x = i % bclim.Width;
- int y = i / bclim.Width;
- img.SetPixel(x, y, c);
- }
- return img;
- }
-
- // BCLIM Data Writing
- internal static int write16BitColorPalette(Bitmap img, ref MemoryStream ms)
- {
- using (Stream pixelcolors = new MemoryStream())
- using (BinaryWriter bz = new BinaryWriter(pixelcolors))
- {
- // Set up our basis.
- bool under16colors = false;
- int colors = getColorCount(img);
- Color[] pcs = new Color[colors];
- if (colors < 16) under16colors = true;
- uint div = 1;
- if (under16colors)
- div = 2;
-
- if (colors > 70) throw new Exception("Too many colors");
-
- // Set up a new reverse image to build into.
- int w = gcm(img.Width, 8);
- int h = gcm(img.Height, 8);
- w = Math.Max(nlpo2(w), nlpo2(h));
- h = w;
- byte[] pixelarray = new Byte[w * h];
-
- const int colorformat = 2;
- int ctr = 1;
-
- pcs[0] = Color.FromArgb(0, 0xFF, 0xFF, 0xFF);
-
- int p = gcm(w, 8) / 8;
- if (p == 0) p = 1;
- int d = 0;
- for (uint i = 0; i < pixelarray.Length; i++)
- {
- d = (int)(i / div);
- // Get Tile Coordinate
- uint x;
- uint y;
- d2xy(i % 64, out x, out y);
-
- // Get Shift Tile
- uint tile = i / 64;
-
- // Shift Tile Coordinate into Tilemap
- x += (uint)(tile % p) * 8;
- y += (uint)(tile / p) * 8;
- if (x >= img.Width || y >= img.Height) // Don't try to access any pixel data outside of our bounds.
- { i++; continue; } // Goto next tile.
-
- // Get Color of Pixel
- Color c = img.GetPixel((int)x, (int)y);
-
- // Color Table Building Logic
- int index = Array.IndexOf(pcs, c);
- if (c.A == 0) index = 0;
- if (index < 0) // If new color
- { pcs[ctr] = c; index = ctr; ctr++; } // Add it to color list
-
- // Add pixel to pixeldata
- if (under16colors) index = index << 4;
- pixelarray[i / div] = (byte)index;
- if (!under16colors) continue;
-
- c = img.GetPixel((int)x + 1, (int)y);
- index = Array.IndexOf(pcs, c);
- if (c.A == 0) index = 0;
- if (index < 0) // If new color
- { pcs[ctr] = c; index = ctr; ctr++; }
- pixelarray[i / div] |= (byte)(index);
- i++;
- }
-
- // Write Intro
- bz.Write((ushort)colorformat); bz.Write((ushort)ctr);
- // Write Colors
- for (int i = 0; i < ctr; i++)
- bz.Write(GetRGBA5551(pcs[i])); // Write byte array.
- // Write Pixel Data
- for (uint i = 0; i < d; i++)
- bz.Write(pixelarray[i]);
- // Write Padding
- while (pixelcolors.Length < nlpo2((int)pixelcolors.Length))
- bz.Write((byte)0);
- // Copy to main CLIM.
- pixelcolors.Position = 0; pixelcolors.CopyTo(ms);
- }
- return 7;
- }
- internal static void writeGeneric(int format, Bitmap img, ref MemoryStream ms)
- {
- int w = img.Width;
- int h = img.Height;
-
- // square format. Yay.
- w = h = Math.Max(nlpo2(w),nlpo2(h));
- if (format == 0 && Math.Min(img.Width, img.Height) > 64)
- {
- w = nlpo2(img.Width);
- h = nlpo2(img.Height);
- }
- using (MemoryStream mz = new MemoryStream())
- using (BinaryWriter bz = new BinaryWriter(mz))
- {
- int p = gcm(w, 8) / 8;
- if (p == 0) p = 1;
- for (uint i = 0; i < w * h; i++)
- {
- uint x;
- uint y;
- d2xy(i % 64, out x, out y);
-
- // Get Shift Tile
- uint tile = i / 64;
-
- // Shift Tile Coordinate into Tilemap
- x += (uint)(tile % p) * 8;
- y += (uint)(tile / p) * 8;
-
- // Don't write data
- Color c;
- if (x >= img.Width || y >= img.Height)
- { c = Color.FromArgb(0, 0, 0, 0); }
- else
- { c = img.GetPixel((int)x, (int)y); if (c.A == 0) c = Color.FromArgb(0, 86, 86, 86); }
-
- switch (format)
- {
- case 0: bz.Write(GetL8(c)); break; // L8
- case 1: bz.Write(GetA8(c)); break; // A8
- case 2: bz.Write(GetLA4(c)); break; // LA4(4)
- case 3: bz.Write(GetLA8(c)); break; // LA8(8)
- case 4: bz.Write(GetHILO8(c)); break; // HILO8
- case 5: bz.Write(GetRGB565(c)); break; // RGB565
- case 6:
- {
- bz.Write(c.B);
- bz.Write(c.G);
- bz.Write(c.R); break;
- }
- case 7: bz.Write(GetRGBA5551(c)); break; // RGBA5551
- case 8: bz.Write(GetRGBA4444(c)); break; // RGBA4444
- case 9: bz.Write(GetRGBA8888(c)); break; // RGBA8
- case 10: throw new Exception("ETC1 not supported.");
- case 11: throw new Exception("ETC1A4 not supported.");
- case 12:
- {
- byte val = (byte)(GetL8(c) / 0x11); // First Pix // L4
- { c = img.GetPixel((int)x, (int)y); if (c.A == 0) c = Color.FromArgb(0, 0, 0, 0); }
- val |= (byte)((GetL8(c) / 0x11) << 4); i++;
- bz.Write(val); break;
- }
- case 13:
- {
- byte val = (byte)(GetA8(c) / 0x11); // First Pix // L4
- { c = img.GetPixel((int)x, (int)y); }
- val |= (byte)((GetA8(c) / 0x11) << 4); i++;
- bz.Write(val); break;
- }
- }
- }
- while (mz.Length < nlpo2((int)mz.Length)) // pad
- bz.Write((byte)0);
- mz.Position = 0;
- mz.CopyTo(ms);
- }
- }
- internal static int getColorCount(Bitmap img)
- {
- Color[] colors = new Color[img.Width * img.Height];
- int colorct = 1;
-
- for (int i = 0; i < colors.Length; i++)
- {
- Color c = img.GetPixel(i % img.Width, i / img.Width);
- int index = Array.IndexOf(colors, c);
- if (c.A == 0) index = 0;
- if (index >= 0) continue;
-
- colors[colorct] = c;
- colorct++;
- }
- return colorct;
- }
-
-
- internal static int[] Convert5To8 = { 0x00,0x08,0x10,0x18,0x20,0x29,0x31,0x39,
- 0x41,0x4A,0x52,0x5A,0x62,0x6A,0x73,0x7B,
- 0x83,0x8B,0x94,0x9C,0xA4,0xAC,0xB4,0xBD,
- 0xC5,0xCD,0xD5,0xDE,0xE6,0xEE,0xF6,0xFF };
-
- private static Color DecodeColor(uint val, int format)
- {
- int alpha = 0xFF, red, green, blue;
- switch (format)
- {
- case 0: // L8
- return Color.FromArgb(alpha, (byte)val, (byte)val, (byte)val);
- case 1: // A8
- return Color.FromArgb((byte)val, alpha, alpha, alpha);
- case 2: // LA4
- red = (byte)(val >> 4);
- alpha = (byte)(val & 0x0F);
- return Color.FromArgb(alpha, red, red, red);
- case 3: // LA8
- red = (byte)((val >> 8 & 0xFF));
- alpha = (byte)(val & 0xFF);
- return Color.FromArgb(alpha, red, red, red);
- case 4: // HILO8
- red = (byte)(val >> 8);
- green = (byte)(val & 0xFF);
- return Color.FromArgb(alpha, red, green, 0xFF);
- case 5: // RGB565
- red = Convert5To8[(val >> 11) & 0x1F];
- green = (byte)(((val >> 5) & 0x3F) * 4);
- blue = Convert5To8[val & 0x1F];
- return Color.FromArgb(alpha, red, green, blue);
- case 6: // RGB8
- red = (byte)((val >> 16) & 0xFF);
- green = (byte)((val >> 8) & 0xFF);
- blue = (byte)(val & 0xFF);
- return Color.FromArgb(alpha, red, green, blue);
- case 7: // RGBA5551
- red = Convert5To8[(val >> 11) & 0x1F];
- green = Convert5To8[(val >> 6) & 0x1F];
- blue = Convert5To8[(val >> 1) & 0x1F];
- alpha = (val & 0x0001) == 1 ? 0xFF : 0x00;
- return Color.FromArgb(alpha, red, green, blue);
- case 8: // RGBA4444
- alpha = (byte)(0x11 * (val & 0xf));
- red = (byte)(0x11 * ((val >> 12) & 0xf));
- green = (byte)(0x11 * ((val >> 8) & 0xf));
- blue = (byte)(0x11 * ((val >> 4) & 0xf));
- return Color.FromArgb(alpha, red, green, blue);
- case 9: // RGBA8888
- red = (byte)((val >> 24) & 0xFF);
- green = (byte)((val >> 16) & 0xFF);
- blue = (byte)((val >> 8) & 0xFF);
- alpha = (byte)(val & 0xFF);
- return Color.FromArgb(alpha, red, green, blue);
- // case 10:
- // case 11:
- case 12: // L4
- return Color.FromArgb(alpha, (byte)(val * 0x11), (byte)(val * 0x11), (byte)(val * 0x11));
- case 13: // A4
- return Color.FromArgb((byte)(val * 0x11), alpha, alpha, alpha);
- default:
- return Color.White;
- }
- }
-
- // Color Conversion
- internal static byte GetL8(Color c)
- {
- byte red = c.R;
- byte green = c.G;
- byte blue = c.B;
- // Luma (Y’) = 0.299 R’ + 0.587 G’ + 0.114 B’ from wikipedia
- return (byte)(((0x4CB2 * red + 0x9691 * green + 0x1D3E * blue) >> 16) & 0xFF);
- } // L8
- internal static byte GetA8(Color c)
- {
- return c.A;
- } // A8
- internal static byte GetLA4(Color c)
- {
- return (byte)((c.A / 0x11) + (c.R / 0x11) << 4);
- } // LA4
- internal static ushort GetLA8(Color c)
- {
- return (ushort)((c.A) + ((c.R) << 8));
- } // LA8
- internal static ushort GetHILO8(Color c)
- {
- return (ushort)((c.G) + ((c.R) << 8));
- } // HILO8
- internal static ushort GetRGB565(Color c)
- {
- int val = 0;
- // val += c.A >> 8; // unused
- val += convert8to5(c.B) >> 3;
- val += (c.G >> 2) << 5;
- val += convert8to5(c.R) << 10;
- return (ushort)val;
- } // RGB565
- // RGB8
- internal static ushort GetRGBA5551(Color c)
- {
- int val = 0;
- val += (byte)(c.A > 0x80 ? 1 : 0);
- val += convert8to5(c.R) << 11;
- val += convert8to5(c.G) << 6;
- val += convert8to5(c.B) << 1;
- ushort v = (ushort)val;
-
- return v;
- }// RGBA5551
- internal static ushort GetRGBA4444(Color c)
- {
- int val = 0;
- val += (c.A / 0x11);
- val += ((c.B / 0x11) << 4);
- val += ((c.G / 0x11) << 8);
- val += ((c.R / 0x11) << 12);
- return (ushort)val;
- }// RGBA4444
- internal static uint GetRGBA8888(Color c) // RGBA8888
- {
- uint val = 0;
- val += c.A;
- val += (uint)(c.B << 8);
- val += (uint)(c.G << 16);
- val += (uint)(c.R << 24);
- return val;
- }
-
- // Unit Conversion
- internal static byte convert8to5(int colorval)
- {
-
- byte[] Convert8to5 = { 0x00,0x08,0x10,0x18,0x20,0x29,0x31,0x39,
- 0x41,0x4A,0x52,0x5A,0x62,0x6A,0x73,0x7B,
- 0x83,0x8B,0x94,0x9C,0xA4,0xAC,0xB4,0xBD,
- 0xC5,0xCD,0xD5,0xDE,0xE6,0xEE,0xF6,0xFF };
- byte i = 0;
- while (colorval > Convert8to5[i]) i++;
- return i;
- }
- internal static UInt32 DM2X(UInt32 code)
- {
- return C11(code >> 0);
- }
- internal static UInt32 DM2Y(UInt32 code)
- {
- return C11(code >> 1);
- }
- internal static UInt32 C11(UInt32 x)
- {
- x &= 0x55555555; // x = -f-e -d-c -b-a -9-8 -7-6 -5-4 -3-2 -1-0
- x = (x ^ (x >> 1)) & 0x33333333; // x = --fe --dc --ba --98 --76 --54 --32 --10
- x = (x ^ (x >> 2)) & 0x0f0f0f0f; // x = ---- fedc ---- ba98 ---- 7654 ---- 3210
- x = (x ^ (x >> 4)) & 0x00ff00ff; // x = ---- ---- fedc ba98 ---- ---- 7654 3210
- x = (x ^ (x >> 8)) & 0x0000ffff; // x = ---- ---- ---- ---- fedc ba98 7654 3210
- return x;
- }
-
- ///
- /// Greatest common multiple (to round up)
- ///
- /// Number to round-up.
- /// Multiple to round-up to.
- /// Rounded up number.
- internal static int gcm(int n, int m)
- {
- return ((n + m - 1) / m) * m;
- }
- ///
- /// Next Largest Power of 2
- ///
- /// Input to round up to next 2^n
- /// 2^n > x && x > 2^(n-1)
- internal static int nlpo2(int x)
- {
- x--; // comment out to always take the next biggest power of two, even if x is already a power of two
- x |= (x >> 1);
- x |= (x >> 2);
- x |= (x >> 4);
- x |= (x >> 8);
- x |= (x >> 16);
- return (x+1);
- }
-
- // Morton Translation
- ///
- /// Combines X/Y Coordinates to a decimal ordinate.
- ///
- ///
- ///
- ///
- internal static uint xy2d(uint x, uint y)
- {
- x &= 0x0000ffff;
- y &= 0x0000ffff;
- x |= (x << 8);
- y |= (y << 8);
- x &= 0x00ff00ff;
- y &= 0x00ff00ff;
- x |= (x << 4);
- y |= (y << 4);
- x &= 0x0f0f0f0f;
- y &= 0x0f0f0f0f;
- x |= (x << 2);
- y |= (y << 2);
- x &= 0x33333333;
- y &= 0x33333333;
- x |= (x << 1);
- y |= (y << 1);
- x &= 0x55555555;
- y &= 0x55555555;
- return x | (y << 1);
- }
- ///
- /// Decimal Ordinate In to X / Y Coordinate Out
- ///
- /// Loop integer which will be decoded to X/Y
- /// Output X coordinate
- /// Output Y coordinate
- internal static void d2xy(uint d, out uint x, out uint y)
- {
- x = d;
- y = (x >> 1);
- x &= 0x55555555;
- y &= 0x55555555;
- x |= (x >> 1);
- y |= (y >> 1);
- x &= 0x33333333;
- y &= 0x33333333;
- x |= (x >> 2);
- y |= (y >> 2);
- x &= 0x0f0f0f0f;
- y &= 0x0f0f0f0f;
- x |= (x >> 4);
- y |= (y >> 4);
- x &= 0x00ff00ff;
- y &= 0x00ff00ff;
- x |= (x >> 8);
- y |= (y >> 8);
- x &= 0x0000ffff;
- y &= 0x0000ffff;
- }
-
- public class BCLIM
- {
- public static CLIM analyze(string path)
- {
- CLIM bclim = new CLIM
- {
- FileName = Path.GetFileNameWithoutExtension(path),
- FilePath = Path.GetDirectoryName(path),
- Extension = Path.GetExtension(path)
- };
- byte[] byteArray = File.ReadAllBytes(path);
- using (BinaryReader br = new BinaryReader(new MemoryStream(byteArray)))
- {
- br.BaseStream.Seek(br.BaseStream.Length - 0x28, SeekOrigin.Begin);
- bclim.Magic = br.ReadUInt32();
-
- bclim.BOM = br.ReadUInt16();
- bclim.CLIMLength = br.ReadUInt32();
- bclim.TileWidth = 2 << br.ReadByte();
- bclim.TileHeight = 2 << br.ReadByte();
- bclim.totalLength = br.ReadUInt32();
- bclim.Count = br.ReadUInt32();
-
- bclim.imag = br.ReadChars(4);
- bclim.imagLength = br.ReadUInt32();
- bclim.Width = br.ReadUInt16();
- bclim.Height = br.ReadUInt16();
- bclim.FileFormat = br.ReadInt32();
- bclim.dataLength = br.ReadUInt32();
-
- bclim.BaseSize = Math.Max(nlpo2(bclim.Width), nlpo2(bclim.Height));
-
- br.BaseStream.Seek(0, SeekOrigin.Begin);
- bclim.Data = br.ReadBytes((int)bclim.dataLength);
-
- return bclim;
- }
- }
- }
- public struct CLIM
- {
- public UInt32 Magic; // CLIM = 0x4D494C43
- public UInt16 BOM; // 0xFFFE
- public UInt32 CLIMLength; // HeaderLength - 14
- public int TileWidth; // 1<<[[n]]
- public int TileHeight; // 1<<[[n]]
- public UInt32 totalLength; // Total Length of file
- public UInt32 Count; // "1" , guessing it's just Count.
-
- public char[] imag; // imag = 0x67616D69
- public UInt32 imagLength; // HeaderLength - 10
- public UInt16 Width; // Final Dimensions
- public UInt16 Height; // Final Dimensions
- public Int32 FileFormat; // ??
- public UInt32 dataLength; // Pixel Data Region Length
-
- public byte[] Data;
-
- public Int32 BaseSize;
-
- //// Contained Data
- //public int ColorFormat;
- //public int ColorCount;
- //public Color[] Colors;
-
- //public int[] Pixels;
-
- public string FileName;
- public string FilePath;
- public string Extension;
- }
-
- // Ericsson Texture Compression 1 - From Ohana3DS
- // https://github.com/gdkchan/Ohana3DS
- // By gdkchan & Reisyukaku, ported to C#.
- // Porting didn't do everything right; TODO: fix.
- internal static byte[] ETC1_Decompress(byte[] Data, int Width, int Height, int Format = 12, int Offset = 0)
- {
- byte[] Temp_Buffer = new byte[((Width * Height) / 2)];
- byte[] Alphas = new byte[Temp_Buffer.Length];
- if (Format == 12)
- {
- Buffer.BlockCopy(Data, Offset, Temp_Buffer, 0, Temp_Buffer.Length);
- for (int j = 0; j <= Alphas.Length - 1; j++)
- {
- Alphas[j] = 0xff;
- }
- }
- else
- {
- int k = 0;
- for (int j = 0; j <= (Width * Height) - 1; j++)
- {
- Buffer.BlockCopy(Data, Offset + j + 8, Temp_Buffer, k, 8);
- Buffer.BlockCopy(Data, Offset + j, Alphas, k, 8);
- k += 8;
- j += 15;
- }
- }
- byte[] Out = new byte[(Width * Height * 4)];
- Offset = 0;
- for (int Y = 0; Y <= (Height / 4) - 1; Y++)
- {
- for (int X = 0; X <= (Width / 4) - 1; X++)
- {
- byte[] Block = new byte[8];
- byte[] Alphas_Block = new byte[8];
- for (int i = 0; i <= 7; i++)
- {
- Block[7 - i] = Data[Offset + i];
- Alphas_Block[i] = Alphas[Offset + i];
- }
- Offset += 8;
- Block = ETC1_Decompress_Block(Block);
-
- bool Low_High_Toggle = false;
- int Alpha_Offset = 0;
- for (int TX = 0; TX <= 3; TX++)
- {
- for (int TY = 0; TY <= 3; TY++)
- {
- int Out_Offset = (X * 4 + TX + ((Y * 4 + TY) * Width)) * 4;
- int Block_Offset = (TX + (TY * 4)) * 4;
- Buffer.BlockCopy(Block, Block_Offset, Out, Out_Offset, 3);
-
- int Alpha_Data = 0;
- if (Low_High_Toggle)
- {
- Alpha_Data = (Alphas_Block[Alpha_Offset] & 0xf0) >> 4;
- Alpha_Offset += 1;
- }
- else
- {
- Alpha_Data = Alphas_Block[Alpha_Offset] & 0xf;
- }
- Low_High_Toggle = !Low_High_Toggle;
- Out[Out_Offset + 3] = Convert.ToByte((Alpha_Data << 4) + Alpha_Data);
- }
- }
- }
- }
- return Out;
- }
- internal static byte[] ETC1_Decompress_Block(byte[] Data)
- {
- //Ericsson Texture Compression
- int Block_Top = BitConverter.ToInt32(Data, 0);
- int Block_Bottom = BitConverter.ToInt32(Data, 4);
-
- bool Flip = (Block_Top & 0x1000000) > 0;
- bool Difference = (Block_Top & 0x2000000) > 0;
-
- int R1, G1, B1;
- int R2, G2, B2;
-
- if (Difference)
- {
- R1 = Block_Top & 0xf8;
- G1 = (Block_Top & 0xf800) >> 8;
- B1 = (Block_Top & 0xf80000) >> 16;
-
- int R = Signed_Byte(Convert.ToByte(R1 >> 3)) + (Signed_Byte(Convert.ToByte((Block_Top & 7) << 5)) >> 5);
- int G = Signed_Byte(Convert.ToByte(G1 >> 3)) + (Signed_Byte(Convert.ToByte((Block_Top & 0x700) >> 3)) >> 5);
- int B = Signed_Byte(Convert.ToByte(B1 >> 3)) + (Signed_Byte(Convert.ToByte((Block_Top & 0x70000) >> 11)) >> 5);
-
- R2 = R;
- G2 = G;
- B2 = B;
-
- R1 = R1 + (R1 >> 5);
- G1 = G1 + (G1 >> 5);
- B1 = B1 + (B1 >> 5);
-
- R2 = (R2 << 3) + (R2 >> 2);
- G2 = (G2 << 3) + (G2 >> 2);
- B2 = (B2 << 3) + (B2 >> 2);
- }
- else
- {
- R1 = Block_Top & 0xf0;
- R1 = R1 + (R1 >> 4);
- G1 = (Block_Top & 0xf000) >> 8;
- G1 = G1 + (G1 >> 4);
- B1 = (Block_Top & 0xf00000) >> 16;
- B1 = B1 + (B1 >> 4);
-
- R2 = (Block_Top & 0xf) << 4;
- R2 = R2 + (R2 >> 4);
- G2 = (Block_Top & 0xf00) >> 4;
- G2 = G2 + (G2 >> 4);
- B2 = (Block_Top & 0xf0000) >> 12;
- B2 = B2 + (B2 >> 4);
- }
-
- int Mod_Table_1 = (Block_Top >> 29) & 7;
- int Mod_Table_2 = (Block_Top >> 26) & 7;
-
- byte[] Out = new byte[(4 * 4 * 4)];
- if (Flip == false)
- {
- for (int Y = 0; Y <= 3; Y++)
- {
- for (int X = 0; X <= 1; X++)
- {
- Color Col_1 = Modify_Pixel(R1, G1, B1, X, Y, Block_Bottom, Mod_Table_1);
- Color Col_2 = Modify_Pixel(R2, G2, B2, X + 2, Y, Block_Bottom, Mod_Table_2);
- Out[(Y * 4 + X) * 4] = Col_1.R;
- Out[((Y * 4 + X) * 4) + 1] = Col_1.G;
- Out[((Y * 4 + X) * 4) + 2] = Col_1.B;
- Out[(Y * 4 + X + 2) * 4] = Col_2.R;
- Out[((Y * 4 + X + 2) * 4) + 1] = Col_2.G;
- Out[((Y * 4 + X + 2) * 4) + 2] = Col_2.B;
- }
- }
- }
- else
- {
- for (int Y = 0; Y <= 1; Y++)
- {
- for (int X = 0; X <= 3; X++)
- {
- Color Col_1 = Modify_Pixel(R1, G1, B1, X, Y, Block_Bottom, Mod_Table_1);
- Color Col_2 = Modify_Pixel(R2, G2, B2, X, Y + 2, Block_Bottom, Mod_Table_2);
- Out[(Y * 4 + X) * 4] = Col_1.R;
- Out[((Y * 4 + X) * 4) + 1] = Col_1.G;
- Out[((Y * 4 + X) * 4) + 2] = Col_1.B;
- Out[((Y + 2) * 4 + X) * 4] = Col_2.R;
- Out[(((Y + 2) * 4 + X) * 4) + 1] = Col_2.G;
- Out[(((Y + 2) * 4 + X) * 4) + 2] = Col_2.B;
- }
- }
- }
-
- return Out;
- }
- internal static byte[] ETC1_Compress(Bitmap Img, int Width, int Height, int Format = 12, bool Show_Warning = true)
- {
- if ((Img.Width != Width) | (Img.Height != Height))
- {
- if (Show_Warning)
- MessageBox.Show("Images need to have the same resolution!", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
- return null;
- }
-
- BitmapData ImgData = Img.LockBits(new Rectangle(0, 0, Img.Width, Img.Height), ImageLockMode.ReadOnly, Img.PixelFormat);
- byte[] Data = new byte[(ImgData.Height * ImgData.Stride)];
- Marshal.Copy(ImgData.Scan0, Data, 0, Data.Length);
- Img.UnlockBits(ImgData);
-
- int BPP = (Img.PixelFormat == PixelFormat.Format32bppArgb) ? 32 : 24;
- byte[] Out_Data = null;
-
- #region Conversion
- switch (Format)
- {
- case 12:
- case 13:
- //Os tiles com compressão ETC1 no 3DS estão embaralhados
- byte[] Out = new byte[(Img.Width * Img.Height * 4)];
- int[] Tile_Scramble = Get_ETC1_Scramble(Img.Width, Img.Height);
-
- int i = 0;
- for (int Tile_Y = 0; Tile_Y <= (Img.Height / 4) - 1; Tile_Y++)
- {
- for (int Tile_X = 0; Tile_X <= (Img.Width / 4) - 1; Tile_X++)
- {
- int TX = Tile_Scramble[i] % (Img.Width / 4);
- int TY = (Tile_Scramble[i] - TX) / (Img.Width / 4);
- for (int Y = 0; Y <= 3; Y++)
- {
- for (int X = 0; X <= 3; X++)
- {
- int Out_Offset = ((TX * 4) + X + ((((TY * 4) + Y)) * Img.Width)) * 4;
- int Image_Offset = ((Tile_X * 4) + X + (((Tile_Y * 4) + Y) * Img.Width)) * (BPP / 8);
-
- Out[Out_Offset] = Data[Image_Offset + 2];
- Out[Out_Offset + 1] = Data[Image_Offset + 1];
- Out[Out_Offset + 2] = Data[Image_Offset];
- if (BPP == 32)
- Out[Out_Offset + 3] = Data[Image_Offset + 3];
- else
- Out[Out_Offset + 3] = 0xff;
- }
- }
- i += 1;
- }
- }
-
-
- Out_Data = new byte[((Img.Width * Img.Height) / Format == 12 ? 2 : 1)];
- int Out_Data_Offset = 0;
-
- for (int Tile_Y = 0; Tile_Y <= (Img.Height / 4) - 1; Tile_Y++)
- {
- for (int Tile_X = 0; Tile_X <= (Img.Width / 4) - 1; Tile_X++)
- {
- bool Flip = false;
- bool Difference = false;
- int Block_Top = 0;
- int Block_Bottom = 0;
-
- //Teste do Difference Bit
- int Diff_Match_V = 0;
- int Diff_Match_H = 0;
- for (int Y = 0; Y <= 3; Y++)
- {
- for (int X = 0; X <= 1; X++)
- {
- int Image_Offset_1 = ((Tile_X * 4) + X + (((Tile_Y * 4) + Y) * Img.Width)) * 4;
- int Image_Offset_2 = ((Tile_X * 4) + (2 + X) + (((Tile_Y * 4) + Y) * Img.Width)) * 4;
-
- byte Bits_R1 = Convert.ToByte(Out[Image_Offset_1] & 0xf8);
- byte Bits_G1 = Convert.ToByte(Out[Image_Offset_1 + 1] & 0xf8);
- byte Bits_B1 = Convert.ToByte(Out[Image_Offset_1 + 2] & 0xf8);
-
- byte Bits_R2 = Convert.ToByte(Out[Image_Offset_2] & 0xf8);
- byte Bits_G2 = Convert.ToByte(Out[Image_Offset_2 + 1] & 0xf8);
- byte Bits_B2 = Convert.ToByte(Out[Image_Offset_2 + 2] & 0xf8);
-
- if ((Bits_R1 == Bits_R2) & (Bits_G1 == Bits_G2) & (Bits_B1 == Bits_B2))
- Diff_Match_V += 1;
- }
- }
- for (int Y = 0; Y <= 1; Y++)
- {
- for (int X = 0; X <= 3; X++)
- {
- int Image_Offset_1 = ((Tile_X * 4) + X + (((Tile_Y * 4) + Y) * Img.Width)) * 4;
- int Image_Offset_2 = ((Tile_X * 4) + X + (((Tile_Y * 4) + (2 + Y)) * Img.Width)) * 4;
-
- byte Bits_R1 = Convert.ToByte(Out[Image_Offset_1] & 0xf8);
- byte Bits_G1 = Convert.ToByte(Out[Image_Offset_1 + 1] & 0xf8);
- byte Bits_B1 = Convert.ToByte(Out[Image_Offset_1 + 2] & 0xf8);
-
- byte Bits_R2 = Convert.ToByte(Out[Image_Offset_2] & 0xf8);
- byte Bits_G2 = Convert.ToByte(Out[Image_Offset_2 + 1] & 0xf8);
- byte Bits_B2 = Convert.ToByte(Out[Image_Offset_2 + 2] & 0xf8);
-
- if ((Bits_R1 == Bits_R2) & (Bits_G1 == Bits_G2) & (Bits_B1 == Bits_B2))
- Diff_Match_H += 1;
- }
- }
- //Difference + Flip
- if (Diff_Match_H == 8)
- {
- Difference = true;
- Flip = true;
- //Difference
- }
- else if (Diff_Match_V == 8)
- {
- Difference = true;
- //Individual
- }
- else
- {
- int Test_R1 = 0;
- int Test_G1 = 0;
- int Test_B1 = 0;
- int Test_R2 = 0;
- int Test_G2 = 0;
- int Test_B2 = 0;
- for (int Y = 0; Y <= 1; Y++)
- {
- for (int X = 0; X <= 1; X++)
- {
- int Image_Offset_1 = ((Tile_X * 4) + X + (((Tile_Y * 4) + Y) * Img.Width)) * 4;
- int Image_Offset_2 = ((Tile_X * 4) + (2 + X) + (((Tile_Y * 4) + (2 + Y)) * Img.Width)) * 4;
-
- Test_R1 += Out[Image_Offset_1];
- Test_G1 += Out[Image_Offset_1 + 1];
- Test_B1 += Out[Image_Offset_1 + 2];
-
- Test_R2 += Out[Image_Offset_2];
- Test_G2 += Out[Image_Offset_2 + 1];
- Test_B2 += Out[Image_Offset_2 + 2];
- }
- }
-
- Test_R1 /= 8;
- Test_G1 /= 8;
- Test_B1 /= 8;
-
- Test_R2 /= 8;
- Test_G2 /= 8;
- Test_B2 /= 8;
-
- int Test_Luma_1 = Convert.ToInt32(0.299f * Test_R1 + 0.587f * Test_G1 + 0.114f * Test_B1);
- int Test_Luma_2 = Convert.ToInt32(0.299f * Test_R2 + 0.587f * Test_G2 + 0.114f * Test_B2);
- int Test_Flip_Diff = Math.Abs(Test_Luma_1 - Test_Luma_2);
- if (Test_Flip_Diff > 48)
- Flip = true;
- }
-
- int Avg_R1 = 0;
- int Avg_G1 = 0;
- int Avg_B1 = 0;
- int Avg_R2 = 0;
- int Avg_G2 = 0;
- int Avg_B2 = 0;
-
- //Primeiro, cálcula a média de cores de cada bloco
- if (Flip)
- {
- for (int Y = 0; Y <= 1; Y++)
- {
- for (int X = 0; X <= 3; X++)
- {
- int Image_Offset_1 = ((Tile_X * 4) + X + (((Tile_Y * 4) + Y) * Img.Width)) * 4;
- int Image_Offset_2 = ((Tile_X * 4) + X + (((Tile_Y * 4) + (2 + Y)) * Img.Width)) * 4;
-
- Avg_R1 += Out[Image_Offset_1];
- Avg_G1 += Out[Image_Offset_1 + 1];
- Avg_B1 += Out[Image_Offset_1 + 2];
-
- Avg_R2 += Out[Image_Offset_2];
- Avg_G2 += Out[Image_Offset_2 + 1];
- Avg_B2 += Out[Image_Offset_2 + 2];
- }
- }
- }
- else
- {
- for (int Y = 0; Y <= 3; Y++)
- {
- for (int X = 0; X <= 1; X++)
- {
- int Image_Offset_1 = ((Tile_X * 4) + X + (((Tile_Y * 4) + Y) * Img.Width)) * 4;
- int Image_Offset_2 = ((Tile_X * 4) + (2 + X) + (((Tile_Y * 4) + Y) * Img.Width)) * 4;
-
- Avg_R1 += Out[Image_Offset_1];
- Avg_G1 += Out[Image_Offset_1 + 1];
- Avg_B1 += Out[Image_Offset_1 + 2];
-
- Avg_R2 += Out[Image_Offset_2];
- Avg_G2 += Out[Image_Offset_2 + 1];
- Avg_B2 += Out[Image_Offset_2 + 2];
- }
- }
- }
-
- Avg_R1 /= 8;
- Avg_G1 /= 8;
- Avg_B1 /= 8;
-
- Avg_R2 /= 8;
- Avg_G2 /= 8;
- Avg_B2 /= 8;
-
- if (Difference)
- {
- //+============+
- //| Difference |
- //+============+
- if ((Avg_R1 & 7) > 3) { Avg_R1 = Clip(Avg_R1 + 8); Avg_R2 = Clip(Avg_R2 + 8); }
- if ((Avg_G1 & 7) > 3) { Avg_G1 = Clip(Avg_G1 + 8); Avg_G2 = Clip(Avg_G2 + 8); }
- if ((Avg_B1 & 7) > 3) { Avg_B1 = Clip(Avg_B1 + 8); Avg_B2 = Clip(Avg_B2 + 8); }
-
- Block_Top = (Avg_R1 & 0xf8) | (((Avg_R2 - Avg_R1) / 8) & 7);
- Block_Top = Block_Top | (((Avg_G1 & 0xf8) << 8) | ((((Avg_G2 - Avg_G1) / 8) & 7) << 8));
- Block_Top = Block_Top | (((Avg_B1 & 0xf8) << 16) | ((((Avg_B2 - Avg_B1) / 8) & 7) << 16));
-
- //Vamos ter certeza de que os mesmos valores obtidos pelo descompressor serão usados na comparação (modo Difference)
- Avg_R1 = Block_Top & 0xf8;
- Avg_G1 = (Block_Top & 0xf800) >> 8;
- Avg_B1 = (Block_Top & 0xf80000) >> 16;
-
- int R = Signed_Byte(Convert.ToByte(Avg_R1 >> 3)) + (Signed_Byte(Convert.ToByte((Block_Top & 7) << 5)) >> 5);
- int G = Signed_Byte(Convert.ToByte(Avg_G1 >> 3)) + (Signed_Byte(Convert.ToByte((Block_Top & 0x700) >> 3)) >> 5);
- int B = Signed_Byte(Convert.ToByte(Avg_B1 >> 3)) + (Signed_Byte(Convert.ToByte((Block_Top & 0x70000) >> 11)) >> 5);
-
- Avg_R2 = R;
- Avg_G2 = G;
- Avg_B2 = B;
-
- Avg_R1 = Avg_R1 + (Avg_R1 >> 5);
- Avg_G1 = Avg_G1 + (Avg_G1 >> 5);
- Avg_B1 = Avg_B1 + (Avg_B1 >> 5);
-
- Avg_R2 = (Avg_R2 << 3) + (Avg_R2 >> 2);
- Avg_G2 = (Avg_G2 << 3) + (Avg_G2 >> 2);
- Avg_B2 = (Avg_B2 << 3) + (Avg_B2 >> 2);
- }
- else
- {
- //+============+
- //| Individual |
- //+============+
- if ((Avg_R1 & 0xf) > 7)
- Avg_R1 = Clip(Avg_R1 + 0x10);
- if ((Avg_G1 & 0xf) > 7)
- Avg_G1 = Clip(Avg_G1 + 0x10);
- if ((Avg_B1 & 0xf) > 7)
- Avg_B1 = Clip(Avg_B1 + 0x10);
- if ((Avg_R2 & 0xf) > 7)
- Avg_R2 = Clip(Avg_R2 + 0x10);
- if ((Avg_G2 & 0xf) > 7)
- Avg_G2 = Clip(Avg_G2 + 0x10);
- if ((Avg_B2 & 0xf) > 7)
- Avg_B2 = Clip(Avg_B2 + 0x10);
-
- Block_Top = ((Avg_R2 & 0xf0) >> 4) | (Avg_R1 & 0xf0);
- Block_Top = Block_Top | (((Avg_G2 & 0xf0) << 4) | ((Avg_G1 & 0xf0) << 8));
- Block_Top = Block_Top | (((Avg_B2 & 0xf0) << 12) | ((Avg_B1 & 0xf0) << 16));
-
- //Vamos ter certeza de que os mesmos valores obtidos pelo descompressor serão usados na comparação (modo Individual)
- Avg_R1 = (Avg_R1 & 0xf0) + ((Avg_R1 & 0xf0) >> 4);
- Avg_G1 = (Avg_G1 & 0xf0) + ((Avg_G1 & 0xf0) >> 4);
- Avg_B1 = (Avg_B1 & 0xf0) + ((Avg_B1 & 0xf0) >> 4);
-
- Avg_R2 = (Avg_R2 & 0xf0) + ((Avg_R2 & 0xf0) >> 4);
- Avg_G2 = (Avg_G2 & 0xf0) + ((Avg_G2 & 0xf0) >> 4);
- Avg_B2 = (Avg_B2 & 0xf0) + ((Avg_B2 & 0xf0) >> 4);
- }
-
- if (Flip)
- Block_Top = Block_Top | 0x1000000;
- if (Difference)
- Block_Top = Block_Top | 0x2000000;
-
- //Seleciona a melhor tabela para ser usada nos blocos
- int Mod_Table_1 = 0;
- int[] Min_Diff_1 = new int[8];
- for (int a = 0; a <= 7; a++)
- {
- Min_Diff_1[a] = 0;
- }
- for (int Y = 0; Y <= (Flip ? 1 : 3); Y++)
- {
- for (int X = 0; X <= (Flip ? 3 : 1); X++)
- {
- int Image_Offset = ((Tile_X * 4) + X + (((Tile_Y * 4) + Y) * Img.Width)) * 4;
- int Luma = Convert.ToInt32(0.299f * Out[Image_Offset] + 0.587f * Out[Image_Offset + 1] + 0.114f * Out[Image_Offset + 2]);
-
- for (int a = 0; a <= 7; a++)
- {
- int Optimal_Diff = 255 * 4;
- for (int b = 0; b <= 3; b++)
- {
- int CR = Clip(Avg_R1 + Modulation_Table[a, b]);
- int CG = Clip(Avg_G1 + Modulation_Table[a, b]);
- int CB = Clip(Avg_B1 + Modulation_Table[a, b]);
-
- int Test_Luma = Convert.ToInt32(0.299f * CR + 0.587f * CG + 0.114f * CB);
- int Diff = Math.Abs(Luma - Test_Luma);
- if (Diff < Optimal_Diff)
- Optimal_Diff = Diff;
- }
- Min_Diff_1[a] += Optimal_Diff;
- }
- }
- }
-
- int Temp_1 = 255 * 8;
- for (int a = 0; a <= 7; a++)
- {
- if (Min_Diff_1[a] < Temp_1)
- {
- Temp_1 = Min_Diff_1[a];
- Mod_Table_1 = a;
- }
- }
-
- int Mod_Table_2 = 0;
- int[] Min_Diff_2 = new int[8];
- for (int a = 0; a <= 7; a++)
- {
- Min_Diff_2[a] = 0;
- }
- for (int Y = Flip ? 2 : 0; Y <= 3; Y++)
- {
- for (int X = Flip ? 0 : 2; X <= 3; X++)
- {
- int Image_Offset = ((Tile_X * 4) + X + (((Tile_Y * 4) + Y) * Img.Width)) * 4;
- int Luma = Convert.ToInt32(0.299f * Out[Image_Offset] + 0.587f * Out[Image_Offset + 1] + 0.114f * Out[Image_Offset + 2]);
-
- for (int a = 0; a <= 7; a++)
- {
- int Optimal_Diff = 255 * 4;
- for (int b = 0; b <= 3; b++)
- {
- int CR = Clip(Avg_R2 + Modulation_Table[a, b]);
- int CG = Clip(Avg_G2 + Modulation_Table[a, b]);
- int CB = Clip(Avg_B2 + Modulation_Table[a, b]);
-
- int Test_Luma = Convert.ToInt32(0.299f * CR + 0.587f * CG + 0.114f * CB);
- int Diff = Math.Abs(Luma - Test_Luma);
- if (Diff < Optimal_Diff)
- Optimal_Diff = Diff;
- }
- Min_Diff_2[a] += Optimal_Diff;
- }
- }
- }
-
- int Temp_2 = 255 * 8;
- for (int a = 0; a <= 7; a++)
- {
- if (Min_Diff_2[a] < Temp_2)
- {
- Temp_2 = Min_Diff_2[a];
- Mod_Table_2 = a;
- }
- }
-
- Block_Top = Block_Top | (Mod_Table_1 << 29);
- Block_Top = Block_Top | (Mod_Table_2 << 26);
-
- //Seleciona o melhor valor da tabela que mais se aproxima com a cor original
- for (int Y = 0; Y <= (Flip ? 1 : 3); Y++)
- {
- for (int X = 0; X <= (Flip ? 3 : 1); X++)
- {
- int Image_Offset = ((Tile_X * 4) + X + (((Tile_Y * 4) + Y) * Img.Width)) * 4;
- int Luma = Convert.ToInt32(0.299f * Out[Image_Offset] + 0.587f * Out[Image_Offset + 1] + 0.114f * Out[Image_Offset + 2]);
-
- int Col_Diff = 255;
- int Pix_Table_Index = 0;
- for (int b = 0; b <= 3; b++)
- {
- int CR = Clip(Avg_R1 + Modulation_Table[Mod_Table_1, b]);
- int CG = Clip(Avg_G1 + Modulation_Table[Mod_Table_1, b]);
- int CB = Clip(Avg_B1 + Modulation_Table[Mod_Table_1, b]);
-
- int Test_Luma = Convert.ToInt32(0.299f * CR + 0.587f * CG + 0.114f * CB);
- int Diff = Math.Abs(Luma - Test_Luma);
- if (Diff < Col_Diff)
- {
- Col_Diff = Diff;
- Pix_Table_Index = b;
- }
- }
-
- int Index = X * 4 + Y;
- if (Index < 8)
- {
- Block_Bottom = Block_Bottom | (((Pix_Table_Index & 2) >> 1) << (Index + 8));
- Block_Bottom = Block_Bottom | ((Pix_Table_Index & 1) << (Index + 24));
- }
- else
- {
- Block_Bottom = Block_Bottom | (((Pix_Table_Index & 2) >> 1) << (Index - 8));
- Block_Bottom = Block_Bottom | ((Pix_Table_Index & 1) << (Index + 8));
- }
- }
- }
-
- for (int Y = Flip ? 2 : 0; Y <= 3; Y++)
- {
- for (int X = Flip ? 0 : 2; X <= 3; X++)
- {
- int Image_Offset = ((Tile_X * 4) + X + (((Tile_Y * 4) + Y) * Img.Width)) * 4;
- int Luma = Convert.ToInt32(0.299f * Out[Image_Offset] + 0.587f * Out[Image_Offset + 1] + 0.114f * Out[Image_Offset + 2]);
-
- int Col_Diff = 255;
- int Pix_Table_Index = 0;
- for (int b = 0; b <= 3; b++)
- {
- int CR = Clip(Avg_R2 + Modulation_Table[Mod_Table_2, b]);
- int CG = Clip(Avg_G2 + Modulation_Table[Mod_Table_2, b]);
- int CB = Clip(Avg_B2 + Modulation_Table[Mod_Table_2, b]);
-
- int Test_Luma = Convert.ToInt32(0.299f * CR + 0.587f * CG + 0.114f * CB);
- int Diff = Math.Abs(Luma - Test_Luma);
- if (Diff < Col_Diff)
- {
- Col_Diff = Diff;
- Pix_Table_Index = b;
- }
- }
-
- int Index = X * 4 + Y;
- if (Index < 8)
- {
- Block_Bottom = Block_Bottom | (((Pix_Table_Index & 2) >> 1) << (Index + 8));
- Block_Bottom = Block_Bottom | ((Pix_Table_Index & 1) << (Index + 24));
- }
- else
- {
- Block_Bottom = Block_Bottom | (((Pix_Table_Index & 2) >> 1) << (Index - 8));
- Block_Bottom = Block_Bottom | ((Pix_Table_Index & 1) << (Index + 8));
- }
- }
- }
-
- //Copia dados para a saída
- byte[] Block = new byte[8];
- Buffer.BlockCopy(BitConverter.GetBytes(Block_Top), 0, Block, 0, 4);
- Buffer.BlockCopy(BitConverter.GetBytes(Block_Bottom), 0, Block, 4, 4);
- byte[] New_Block = new byte[8];
- for (int j = 0; j <= 7; j++)
- {
- New_Block[7 - j] = Block[j];
- }
- if (Format == 13)
- {
- byte[] Alphas = new byte[8];
- int Alpha_Offset = 0;
- for (int TX = 0; TX <= 3; TX++)
- {
- for (int TY = 0; TY <= 3; TY += 2)
- {
- int Img_Offset_1 = (Tile_X * 4 + TX + ((Tile_Y * 4 + TY) * Img.Width)) * 4;
- int Img_Offset_2 = (Tile_X * 4 + TX + ((Tile_Y * 4 + TY + 1) * Img.Width)) * 4;
-
- byte Alpha_1 = (byte)(Out[Img_Offset_1 + 3] >> 4);
- byte Alpha_2 = (byte)(Out[Img_Offset_2 + 3] >> 4);
-
- Alphas[Alpha_Offset] = (byte)(Alpha_1 | (Alpha_2 << 4));
-
- Alpha_Offset += 1;
- }
- }
-
- Buffer.BlockCopy(Alphas, 0, Out_Data, Out_Data_Offset, 8);
- Buffer.BlockCopy(New_Block, 0, Out_Data, Out_Data_Offset + 8, 8);
- Out_Data_Offset += 16;
- }
- else if (Format == 12)
- {
- Buffer.BlockCopy(New_Block, 0, Out_Data, Out_Data_Offset, 8);
- Out_Data_Offset += 8;
- }
-
- // Texture_Insertion_Percentage = Convert.ToSingle((Out_Data_Offset / Out_Data.Length) * 100);
- }
- }
- break;
- }
- #endregion
- // Texture_Insertion_Percentage = 0;
- return Out_Data;
- }
- #region Misc Functions
- internal static sbyte Signed_Byte(byte Byte_To_Convert)
- {
- if ((Byte_To_Convert < 0x80))
- return Convert.ToSByte(Byte_To_Convert);
- return Convert.ToSByte(Byte_To_Convert - 0x100);
- }
- internal static int Signed_Short(int Short_To_Convert)
- {
- if ((Short_To_Convert < 0x8000))
- return Short_To_Convert;
- return Short_To_Convert - 0x10000;
- }
- internal static byte[] Mirror_Texture(byte[] Data, int Width, int Height)
- {
- byte[] Out = new byte[((Width * 2) * Height * 4)];
- for (int Y = 0; Y <= Height - 1; Y++)
- {
- for (int X = 0; X <= Width - 1; X++)
- {
- int Offset = (X + (Y * Width)) * 4;
- int Offset_2 = (X + (Y * (Width * 2))) * 4;
- int Offset_3 = ((Width + (Width - X - 1)) + (Y * (Width * 2))) * 4;
- Buffer.BlockCopy(Data, Offset, Out, Offset_2, 4);
- Buffer.BlockCopy(Data, Offset, Out, Offset_3, 4);
- }
- }
- return Out;
- }
- internal static bool Check_Alpha(byte[] Img)
- {
- for (int Offset = 0; Offset <= Img.Length - 1; Offset += 4)
- {
- if (Img[Offset + 3] < 0xff)
- return true;
- }
- return false;
- }
- internal static int[] Get_ETC1_Scramble(int Width, int Height)
- {
- int[] Tile_Scramble = new int[((Width / 4) * (Height / 4))];
- int Base_Accumulator = 0;
- int Line_Accumulator = 0;
- int Base_Number = 0;
- int Line_Number = 0;
-
- for (int Tile = 0; Tile <= Tile_Scramble.Length - 1; Tile++)
- {
- if ((Tile % (Width / 4) == 0) & Tile > 0)
- {
- if (Line_Accumulator < 1)
- {
- Line_Accumulator += 1;
- Line_Number += 2;
- Base_Number = Line_Number;
- }
- else
- {
- Line_Accumulator = 0;
- Base_Number -= 2;
- Line_Number = Base_Number;
- }
- }
-
- Tile_Scramble[Tile] = Base_Number;
-
- if (Base_Accumulator < 1)
- {
- Base_Accumulator += 1;
- Base_Number += 1;
- }
- else
- {
- Base_Accumulator = 0;
- Base_Number += 3;
- }
- }
-
- return Tile_Scramble;
- }
- internal static Color Modify_Pixel(int R, int G, int B, int X, int Y, int Mod_Block, int Mod_Table)
- {
- int Index = X * 4 + Y;
- int Pixel_Modulation = 0;
- int MSB = Mod_Block << 1;
-
- Pixel_Modulation = Index < 8
- ? Modulation_Table[Mod_Table, ((Mod_Block >> (Index + 24)) & 1) + ((MSB >> (Index + 8)) & 2)]
- : Modulation_Table[Mod_Table, ((Mod_Block >> (Index + 8)) & 1) + ((MSB >> (Index - 8)) & 2)];
-
- R = Clip(R + Pixel_Modulation);
- G = Clip(G + Pixel_Modulation);
- B = Clip(B + Pixel_Modulation);
-
- return Color.FromArgb(B, G, R);
- }
- internal static byte Clip(int Value)
- {
- if (Value > 0xff)
- return 0xff;
- else if (Value < 0)
- return 0;
- else
- return Convert.ToByte(Value & 0xff);
- }
- internal static int[,] Modulation_Table =
- {
- { 2, 8, 2, 8 },
- { 5, 17, 5, 17 },
- { 9, 29, 9, 29 },
- { 13, 42, 13, 42 },
- { 18, 60, 18, 60 },
- { 24, 80, 24, 80 },
- { 33, 106, 33, -106 },
- { 47, 183, 47, 183 }
- };
- #endregion
- }
-}