Refactoring

Also included the new GARC handler which can now process all GARCs.
This commit is contained in:
Kurt
2015-06-16 00:37:11 -07:00
parent c96ae87877
commit cfd91b214a
14 changed files with 1542 additions and 2191 deletions

754
pk3DS/3DS/BCLIM.cs Normal file
View File

@@ -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;
}
/// <summary>
/// Greatest common multiple (to round up)
/// </summary>
/// <param name="n">Number to round-up.</param>
/// <param name="m">Multiple to round-up to.</param>
/// <returns>Rounded up number.</returns>
internal static int gcm(int n, int m)
{
return ((n + m - 1) / m) * m;
}
/// <summary>
/// Next Largest Power of 2
/// </summary>
/// <param name="x">Input to round up to next 2^n</param>
/// <returns>2^n > x && x > 2^(n-1) </returns>
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
/// <summary>
/// Combines X/Y Coordinates to a decimal ordinate.
/// </summary>
/// <param name="x"></param>
/// <param name="y"></param>
/// <returns></returns>
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);
}
/// <summary>
/// Decimal Ordinate In to X / Y Coordinate Out
/// </summary>
/// <param name="d">Loop integer which will be decoded to X/Y</param>
/// <param name="x">Output X coordinate</param>
/// <param name="y">Output Y coordinate</param>
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;
}
}
}

View File

@@ -24,7 +24,7 @@
using System.IO;
using System.Windows.Forms;
namespace BLZ
namespace CTR
{
public class BLZCoder
{

6
pk3DS/3DS/ETC1.cs Normal file
View File

@@ -0,0 +1,6 @@
namespace CTR
{
class ETC1
{
}
}

503
pk3DS/3DS/GARC.cs Normal file
View File

@@ -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
}

View File

@@ -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)
/// <returns>The length of the output data.</returns>
/// <exception cref="NotEnoughDataException">When the given length of the input data
/// is not enough to properly decompress the input.</exception>
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)
/// </summary>
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.
/// </summary>
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.</param>
/// <param name="disps">The 'disp' values of the compressed blocks. May be 0, in which case the
/// corresponding length will never be anything other than 1.</param>
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
}
}

228
pk3DS/3DS/mini.cs Normal file
View File

@@ -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; }
}
}
}

View File

@@ -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)
{

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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};

View File

@@ -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
//

View File

@@ -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;
}
}
}
}

View File

@@ -52,14 +52,17 @@
<ItemGroup>
<Compile Include="3DS\AES.cs" />
<Compile Include="3DS\DARC.cs" />
<Compile Include="3DS\ETC1.cs" />
<Compile Include="3DS\Exheader.cs" />
<Compile Include="3DS\LZSS.cs" />
<Compile Include="3DS\mini.cs" />
<Compile Include="3DS\NCCH.cs" />
<Compile Include="3DS\NCSD.cs" />
<Compile Include="ARCUtil.cs" />
<Compile Include="3DS\BLZ.cs" />
<Compile Include="3DS\CTR.cs" />
<Compile Include="3DS\ExeFS.cs" />
<Compile Include="png2bclim.cs" />
<Compile Include="3DS\BCLIM.cs" />
<Compile Include="Randomizer.cs" />
<Compile Include="3DS\RomFS.cs" />
<Compile Include="Subforms\TitleScreen.cs">
@@ -146,7 +149,7 @@
<Compile Include="Subforms\EggMove.Designer.cs">
<DependentUpon>EggMove.cs</DependentUpon>
</Compile>
<Compile Include="GARCTool.cs" />
<Compile Include="3DS\GARC.cs" />
<Compile Include="Legal.cs" />
<Compile Include="Main.cs">
<SubType>Form</SubType>

File diff suppressed because it is too large Load Diff