public byte[] Save() { byte[][] data = new byte[FileCount][]; for (int i = 0; i < data.Length; i++) { if (Storage[i] == null || !Storage[i].Saved) // retrieve original { data[i] = getFile(i, 0); } else // use modified { data[i] = Storage[i].Save(); } } var ng = packGARC(data, garc.Version, (int)garc.ContentPadToNearest); garc = ng.garc; Data = ng.Data; return(Data); }
public lzGARC(byte[] data) { Data = data; garc = unpackGARC(data); Storage = new GARCEntry[FileCount]; }
public static MemGARC packGARC(byte[][] data, int version, int contentpadnearest) { if (contentpadnearest < 0) { contentpadnearest = 4; } // Set Up the GARC template. GARCFile garc = new GARCFile { ContentPadToNearest = (uint)contentpadnearest, fato = { // Magic = new[] { 'O', 'T', 'A', 'F' }, Entries = new FATO_Entry[data.Length], EntryCount = (ushort)data.Length, HeaderSize = 0xC + (data.Length * 4), Padding = 0xFFFF }, fatb = { // Magic = new[] { 'B', 'T', 'A', 'F' }, Entries = new FATB_Entry[data.Length], FileCount = data.Length } }; if (version == VER_6) { garc.ContentPadToNearest = 4; } int op = 0; int od = 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 garc.fatb.Entries[i].IsFolder = false; garc.fatb.Entries[i].SubEntries[0].Exists = true; // Assemble Entry var paddingRequired = data[i].Length % garc.ContentPadToNearest; if (paddingRequired != 0) { paddingRequired = garc.ContentPadToNearest - paddingRequired; } int actualLength = data[i].Length + (int)paddingRequired; 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 = data[i].Length; garc.fatb.Entries[i].SubEntries[0].Padding = (int)paddingRequired; od += actualLength; op += 12; garc.fatb.Entries[i].Vector = 1; } garc.fatb.HeaderSize = 0xC + op; // Set up the Header Info string tempFile = Path.GetTempFileName(); using (var newGARC = new FileStream(tempFile, FileMode.Create)) using (BinaryWriter gw = new BinaryWriter(newGARC)) { #region Write GARC Headers // Write GARC gw.Write((uint)0x47415243); // GARC gw.Write((uint)(version == VER_6 ? 0x24 : 0x1C)); // Header Length gw.Write((ushort)0xFEFF); // Endianness BOM gw.Write((ushort)version); // Version 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) if (version == VER_6) { gw.Write((uint)0x0); gw.Write((uint)0x0); } // 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); } } gw.Write((uint)0x46494D42); // FIMB gw.Write((uint)0x0000000C); // Header Length var dataLen = gw.BaseStream.Position; gw.Write((uint)0); // Data Length - TEMP gw.Seek(0x10, SeekOrigin.Begin); // Goto the start of the un-set 0 data we set earlier and set it. var hdrLen = gw.BaseStream.Position; gw.Write((uint)0); // Write Data Offset - TEMP gw.Write((uint)0); // Write total GARC Length - TEMP // Write Handling information if (version == VER_4) { gw.Write(garc.ContentLargestUnpadded); // Write Largest File stat } else if (version == VER_6) { gw.Write(garc.ContentLargestPadded); // Write Largest With Padding gw.Write(garc.ContentLargestUnpadded); // Write Largest Without Padding gw.Write(garc.ContentPadToNearest); } newGARC.Seek(0, SeekOrigin.End); // Goto the end so we can copy the filedata after the GARC headers. #endregion #region Write Files var ghLength = gw.BaseStream.Length; long largestSize = 0; // Required memory to allocate to handle the largest file long largestPadded = 0; // Required memory to allocate to handle the largest PADDED file (Ver6 only) for (int i = 0; i < data.Length; i++) { byte[] e = data[i]; // Update largest file length if necessary int len = e.Length; int padding = garc.fatb.Entries[i].SubEntries[0].Padding; bool largest = len > largestSize; if (largest) { largestSize = len; largestPadded = len + padding; } // Write to FIMB gw.Write(e); // Pad with FF (unused byte) while (padding-- > 0) { gw.Write((byte)0xFF); } } garc.ContentLargestUnpadded = (uint)largestSize; garc.ContentLargestPadded = (uint)largestPadded; var gdLength = gw.BaseStream.Length - ghLength; #endregion gw.Seek((int)dataLen, SeekOrigin.Begin); gw.Write((uint)gdLength); // Data Length gw.Seek((int)hdrLen, SeekOrigin.Begin); gw.Write((uint)ghLength); // Write Data Offset gw.Write((uint)gw.BaseStream.Length); // Write total GARC Length if (version == VER_4) { gw.Write(garc.ContentLargestUnpadded); // Write Largest File stat } else if (version == VER_6) { gw.Write(garc.ContentLargestPadded); // Write Largest With Padding gw.Write(garc.ContentLargestUnpadded); // Write Largest Without Padding gw.Write(garc.ContentPadToNearest); } } byte[] GARCdata = File.ReadAllBytes(tempFile); File.Delete(tempFile); return(new MemGARC(GARCdata)); }
public MemGARC(byte[] data) { Data = data; garc = unpackGARC(data); }
public static int garcPackMS(string folderPath, string garcPath, int version, int bytesPadding) { // Check to see if our input folder exists. if (!new DirectoryInfo(folderPath).Exists) { throw new DirectoryNotFoundException("Folder does not exist"); } if (version != VER_4 && version != VER_6) { throw new FormatException("Invalid GARC Version: 0x" + version.ToString("X4")); } // 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 ? int.Parse(fn) : int.Parse(fn.Substring(compressed + 4)); packOrder[fileNumber] = f; filectr++; } foreach (string f in folders) { packOrder[int.Parse(new DirectoryInfo(f).Name)] = f; filectr += Directory.GetFiles(f).Length; } } catch (Exception e) { throw new Exception("Invalid packing filenames", e); } FileCountDetermined?.Invoke(null, new FileCountDeterminedEventArgs { Total = filectr }); #endregion // Set Up the GARC template. GARCFile garc = new GARCFile { ContentPadToNearest = 4, 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 } }; if (version == VER_6) { // Some files have larger bytes-to-pad values (ex/ 0x80 for a109) // Hopefully there's no problems defining this with a constant number. garc.ContentPadToNearest = 4; } #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 ? int.Parse(fn) : int.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; // Step PackProgressed?.Invoke(null, new PackProgressedEventArgs { Current = index++, Total = filectr, CurrentFile = packOrder[i] }); } 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 ? int.Parse(fn) : int.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; // Step PackProgressed?.Invoke(null, new PackProgressedEventArgs { Current = index++, Total = filectr, CurrentFile = packOrder[i] }); } } 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 (var newGARC = new FileStream(garcPath, FileMode.Create)) using (BinaryWriter gw = new BinaryWriter(newGARC)) { #region Write GARC Headers // Write GARC gw.Write((uint)0x47415243); // GARC gw.Write((uint)(version == VER_6 ? 0x24 : 0x1C)); // Header Length gw.Write((ushort)0xFEFF); // Endianness BOM gw.Write((ushort)version); // Version 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) if (version == VER_6) { gw.Write((uint)0x0); gw.Write((uint)0x0); } // 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); } } // Write FIMB gw.Write((uint)0x46494D42); // FIMB gw.Write((uint)0x0000000C); // Header Length var dataLen = gw.BaseStream.Position; gw.Write((uint)0); // Data Length - TEMP gw.Seek(0x10, SeekOrigin.Begin); // Goto the start of the un-set 0 data we set earlier and set it. var hdrLen = gw.BaseStream.Position; gw.Write((uint)0); // Write Data Offset - TEMP gw.Write((uint)0); // Write total GARC Length - TEMP // Write Handling information if (version == VER_4) { gw.Write(garc.ContentLargestUnpadded); // Write Largest File stat } else if (version == VER_6) { gw.Write(garc.ContentLargestPadded); // Write Largest With Padding gw.Write(garc.ContentLargestUnpadded); // Write Largest Without Padding gw.Write(garc.ContentPadToNearest); } newGARC.Seek(0, SeekOrigin.End); // Goto the end so we can copy the filedata after the GARC headers. #endregion #region Write Files var ghLength = gw.BaseStream.Length; long largestSize = 0; // Required memory to allocate to handle the largest file long largestPadded = 0; // Required memory to allocate to handle the largest PADDED file (Ver6 only) 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; int padding = (int)(len % bytesPadding); if (padding != 0) { padding = bytesPadding - padding; } bool largest = len > largestSize; if (largest) { largestSize = len; largestPadded = len + padding; } // Write to FIMB using (var x = File.OpenRead(f)) x.CopyTo(newGARC); // While length is not divisible by 4, pad with FF (unused byte) while (padding-- > 0) { gw.Write((byte)0xFF); } } } garc.ContentLargestUnpadded = (uint)largestSize; garc.ContentLargestPadded = (uint)largestPadded; var gdLength = gw.BaseStream.Length - ghLength; #endregion gw.Seek((int)dataLen, SeekOrigin.Begin); gw.Write((uint)gdLength); // Data Length gw.Seek((int)hdrLen, SeekOrigin.Begin); gw.Write((uint)ghLength); // Write Data Offset gw.Write((uint)gw.BaseStream.Length); // Write total GARC Length // Write Handling information if (version == VER_4) { gw.Write(garc.ContentLargestUnpadded); // Write Largest File stat } else if (version == VER_6) { gw.Write(garc.ContentLargestPadded); // Write Largest With Padding gw.Write(garc.ContentLargestUnpadded); // Write Largest Without Padding gw.Write(garc.ContentPadToNearest); } return(filectr); } }
private static GARCFile unpackGARC(Stream stream) { GARCFile garc = new GARCFile(); using (BinaryReader br = new BinaryReader(stream)) { // GARC Header garc.Magic = br.ReadChars(4); garc.HeaderSize = br.ReadUInt32(); garc.Endianess = br.ReadUInt16(); garc.Version = br.ReadUInt16(); garc.ChunkCount = br.ReadUInt32(); garc.DataOffset = br.ReadUInt32(); garc.FileSize = br.ReadUInt32(); if (garc.Version == VER_4) { garc.ContentLargestUnpadded = br.ReadUInt32(); garc.ContentPadToNearest = 4; } else if (garc.Version == VER_6) { garc.ContentLargestPadded = br.ReadUInt32(); garc.ContentLargestUnpadded = br.ReadUInt32(); garc.ContentPadToNearest = br.ReadUInt32(); } else { throw new FormatException("Invalid GARC Version: 0x" + garc.Version.ToString("X4")); } if (garc.ChunkCount != 4) { throw new FormatException("Invalid GARC Chunk Count: " + garc.ChunkCount); } // 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 static int garcUnpack(string garcPath, string outPath, bool skipDecompression) { if (!File.Exists(garcPath)) { throw new FileNotFoundException("File does not exist"); } // 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)); if (outPath == "gametext") { format = "D3"; } FileCountDetermined?.Invoke(null, new FileCountDeterminedEventArgs { Total = fileCount }); 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) { throw new Exception("A compressed file could not be deleted: " + fileOut, e); } } catch { // File is really not encrypted. File.Delete(decout); } } #endregion UnpackProgressed?.Invoke(null, new UnpackProgressedEventArgs { Current = filectr, Total = fileCount }); if ((vector >>= 1) == 0) { break; } } } } return(fileCount); }
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 GARCFile garc = unpackGARC(garcPath); const string ext = "bin"; // Default Extension Name int fileCount = garc.fatb.FileCount; string format = "D" + Math.Ceiling(Math.Log10(fileCount)); if (outPath == "gametext") { format = "D3"; } #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 = $"{filectr/fileCount:P2} - {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); }
public static MemGARC packGARC(byte[][] data, int version, int contentpadnearest) { if (contentpadnearest < 0) { contentpadnearest = 4; } // Set Up the GARC template. GARCFile garc = new GARCFile { ContentPadToNearest = (uint)contentpadnearest, fato = { // Magic = new[] { 'O', 'T', 'A', 'F' }, Entries = new FATO_Entry[data.Length], EntryCount = (ushort)data.Length, HeaderSize = 0xC + data.Length * 4, Padding = 0xFFFF }, fatb = { // Magic = new[] { 'B', 'T', 'A', 'F' }, Entries = new FATB_Entry[data.Length], FileCount = data.Length } }; if (version == VER_6) { garc.ContentPadToNearest = 4; } int op = 0; int od = 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 garc.fatb.Entries[i].IsFolder = false; garc.fatb.Entries[i].SubEntries[0].Exists = true; // Assemble Entry int actualLength = data[i].Length % 4 == 0 ? data[i].Length : data[i].Length + 4 - data[i].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 = data[i].Length; od += actualLength; op += 12; garc.fatb.Entries[i].Vector = 1; } garc.fatb.HeaderSize = 0xC + op; // 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)(version == VER_6 ? 0x24 : 0x1C)); // Header Length gw.Write((ushort)0xFEFF); // Endianness BOM gw.Write((ushort)version); // Version 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) if (version == VER_6) { gw.Write((uint)0x0); gw.Write((uint)0x0); } // 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 long largestPadded = 0; // Required memory to allocate to handle the largest PADDED file (Ver6 only) foreach (byte[] e in data) { // Update largest file length if necessary long len = e.Length; bool largest = len > largestSize; if (largest) { largestSize = len; largestPadded = len; } // Write to FIMB using (var input = new MemoryStream(e)) input.CopyTo(GARCdata); // While length is not divisible by 4, pad with FF (unused byte) while (GARCdata.Length % garc.ContentPadToNearest != 0) { GARCdata.WriteByte(0xFF); if (largest) { largestPadded++; } } } garc.ContentLargestUnpadded = (uint)largestSize; garc.ContentLargestPadded = (uint)largestPadded; #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 // Write Handling information if (version == VER_4) { gw.Write(garc.ContentLargestUnpadded); // Write Largest File stat } else if (version == VER_6) { gw.Write(garc.ContentLargestPadded); // Write Largest With Padding gw.Write(garc.ContentLargestUnpadded); // Write Largest Without Padding gw.Write(garc.ContentPadToNearest); } 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) return(new MemGARC(newGARC.ToArray())); } }
public static bool garcPackMS(string folderPath, string garcPath, int version, 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); } if (version != VER_4 && version != VER_6) { throw new FormatException("Invalid GARC Version: 0x" + version.ToString("X4")); } // 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 ? int.Parse(fn) : int.Parse(fn.Substring(compressed + 4)); packOrder[fileNumber] = f; filectr++; } foreach (string f in folders) { packOrder[int.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 { ContentPadToNearest = 4, 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 } }; if (version == VER_6) { // Some files have larger bytes-to-pad values (ex/ 0x80 for a109) // Hopefully there's no problems defining this with a constant number. garc.ContentPadToNearest = 4; } #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 ? int.Parse(fn) : int.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 = $"{(float) index/(float) filectr:P2} - {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 ? int.Parse(fn) : int.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 = $"{(float) index/(float) filectr:P2} - {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)(version == VER_6 ? 0x24 : 0x1C)); // Header Length gw.Write((ushort)0xFEFF); // Endianness BOM gw.Write((ushort)version); // Version 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) if (version == VER_6) { gw.Write((uint)0x0); gw.Write((uint)0x0); } // 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 long largestPadded = 0; // Required memory to allocate to handle the largest PADDED file (Ver6 only) 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; bool largest = len > largestSize; if (largest) { largestSize = len; largestPadded = 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 % garc.ContentPadToNearest != 0) { GARCdata.WriteByte(0xFF); if (largest) { largestPadded++; } } } } garc.ContentLargestUnpadded = (uint)largestSize; garc.ContentLargestPadded = (uint)largestPadded; #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 // Write Handling information if (version == VER_4) { gw.Write(garc.ContentLargestUnpadded); // Write Largest File stat } else if (version == VER_6) { gw.Write(garc.ContentLargestPadded); // Write Largest With Padding gw.Write(garc.ContentLargestUnpadded); // Write Largest Without Padding gw.Write(garc.ContentPadToNearest); } 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 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); }