From 9b253c8aa4030f8b7274bf3e7e3d6a233990ac0a Mon Sep 17 00:00:00 2001 From: Kurt Date: Thu, 19 Jan 2017 23:05:38 -0800 Subject: [PATCH] Update garc packing from files Closes #95, adds padding length option for files that may need it --- pk3DS/3DS/GARC.cs | 80 ++++++++++++++++++++++----------- pk3DS/Main.cs | 10 ++--- pk3DS/Tools/ToolsUI.Designer.cs | 35 +++++++++++++++ pk3DS/Tools/ToolsUI.cs | 9 ++-- 4 files changed, 100 insertions(+), 34 deletions(-) diff --git a/pk3DS/3DS/GARC.cs b/pk3DS/3DS/GARC.cs index 2493119..cb390db 100644 --- a/pk3DS/3DS/GARC.cs +++ b/pk3DS/3DS/GARC.cs @@ -13,7 +13,7 @@ public static class GARC public const ushort VER_6 = 0x0600; public const ushort VER_4 = 0x0400; - public static bool garcPackMS(string folderPath, string garcPath, int version, ProgressBar pBar1 = null, Label label = null, bool supress = false) + public static bool garcPackMS(string folderPath, string garcPath, int version, int bytesPadding, 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; } @@ -194,8 +194,7 @@ public static bool garcPackMS(string folderPath, string garcPath, int version, P catch { } // Set up the Header Info - using (MemoryStream newGARC = new MemoryStream()) - using (MemoryStream GARCdata = new MemoryStream()) + using (var newGARC = new FileStream(garcPath, FileMode.Create)) using (BinaryWriter gw = new BinaryWriter(newGARC)) { #region Write GARC Headers @@ -233,9 +232,37 @@ public static bool garcPackMS(string folderPath, string garcPath, int version, P 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) @@ -245,37 +272,34 @@ public static bool garcPackMS(string folderPath, string garcPath, int version, P { // 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; + largestPadded = len + padding; } // Write to FIMB - using (var input = File.OpenRead(f)) - input.CopyTo(GARCdata); + using (var x = File.OpenRead(f)) + x.CopyTo(newGARC); // While length is not divisible by 4, pad with FF (unused byte) - while (GARCdata.Length%garc.ContentPadToNearest != 0) - { - GARCdata.WriteByte(0xFF); - if (largest) - largestPadded++; - } + while (padding-- > 0) + gw.Write((byte)0xFF); } } garc.ContentLargestUnpadded = (uint)largestSize; garc.ContentLargestPadded = (uint)largestPadded; + var gdLength = gw.BaseStream.Length - ghLength; #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.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) @@ -289,15 +313,8 @@ public static bool garcPackMS(string folderPath, string garcPath, int version, P 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; } @@ -672,6 +689,17 @@ public static MemGARC packGARC(byte[][] data, int version, int contentpadnearest 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); diff --git a/pk3DS/Main.cs b/pk3DS/Main.cs index 33087d3..01c8efe 100644 --- a/pk3DS/Main.cs +++ b/pk3DS/Main.cs @@ -799,7 +799,7 @@ private void fileSet(IEnumerable files, bool keep = false) { string GARC = Config.getGARCFileName(toEdit); updateStatus($"GARC Set: {toEdit} @ {GARC}... "); - threadSet(Path.Combine(RomFSPath, GARC), toEdit); + threadSet(Path.Combine(RomFSPath, GARC), toEdit, 4); // 4 bytes for Gen6 while (threads > 0) Thread.Sleep(50); if (!keep && Directory.Exists(toEdit)) Directory.Delete(toEdit, true); resetStatus(); @@ -1144,14 +1144,14 @@ private bool getGARC(string infile, string outfolder, bool PB, bool bypassExt = } catch (Exception e) { Util.Error("Could not get the GARC:", e.ToString()); threads--; return false; } } - private bool setGARC(string outfile, string infolder, bool PB) + private bool setGARC(string outfile, string infolder, int padBytes, bool PB) { if (skipBoth || (ModifierKeys == Keys.Control && Util.Prompt(MessageBoxButtons.YesNo, "Cancel writing data back to GARC?") == DialogResult.Yes)) { threads--; updateStatus("Aborted!", false); return false; } try { - bool success = CTR.GARC.garcPackMS(infolder, outfile, Config.GARCVersion, PB ? pBar1 : null, null, true); + bool success = CTR.GARC.garcPackMS(infolder, outfile, Config.GARCVersion, padBytes, PB ? pBar1 : null, null, true); threads--; updateStatus(string.Format(success ? "Success!" : "Failed!"), false); return success; @@ -1165,10 +1165,10 @@ private void threadGet(string infile, string outfolder, bool PB = true, bool byp catch { } new Thread(() => getGARC(infile, outfolder, PB, bypassExt)).Start(); } - private void threadSet(string outfile, string infolder, bool PB = true) + private void threadSet(string outfile, string infolder, int padBytes, bool PB = true) { threads++; - new Thread(() => setGARC(outfile, infolder, PB)).Start(); + new Thread(() => setGARC(outfile, infolder, padBytes, PB)).Start(); } private static void backupGARCs(bool overwrite, params string[] g) diff --git a/pk3DS/Tools/ToolsUI.Designer.cs b/pk3DS/Tools/ToolsUI.Designer.cs index 865bb87..e1cb703 100644 --- a/pk3DS/Tools/ToolsUI.Designer.cs +++ b/pk3DS/Tools/ToolsUI.Designer.cs @@ -39,7 +39,10 @@ private void InitializeComponent() this.B_Reset = new System.Windows.Forms.Button(); this.CHK_Delete = new System.Windows.Forms.CheckBox(); this.pBar1 = new System.Windows.Forms.ProgressBar(); + this.NUD_Padding = new System.Windows.Forms.NumericUpDown(); + this.L_Padding = new System.Windows.Forms.Label(); ((System.ComponentModel.ISupportInitialize)(this.PB_BCLIM)).BeginInit(); + ((System.ComponentModel.ISupportInitialize)(this.NUD_Padding)).BeginInit(); this.SuspendLayout(); // // PB_Unpack @@ -147,12 +150,41 @@ private void InitializeComponent() this.pBar1.Size = new System.Drawing.Size(300, 12); this.pBar1.TabIndex = 11; // + // NUD_Padding + // + this.NUD_Padding.Hexadecimal = true; + this.NUD_Padding.Location = new System.Drawing.Point(118, 164); + this.NUD_Padding.Maximum = new decimal(new int[] { + 65536, + 0, + 0, + 0}); + this.NUD_Padding.Name = "NUD_Padding"; + this.NUD_Padding.Size = new System.Drawing.Size(44, 20); + this.NUD_Padding.TabIndex = 12; + this.NUD_Padding.Value = new decimal(new int[] { + 4, + 0, + 0, + 0}); + // + // L_Padding + // + this.L_Padding.Location = new System.Drawing.Point(12, 164); + this.L_Padding.Name = "L_Padding"; + this.L_Padding.Size = new System.Drawing.Size(100, 20); + this.L_Padding.TabIndex = 13; + this.L_Padding.Text = "GARC Padding:"; + this.L_Padding.TextAlign = System.Drawing.ContentAlignment.MiddleRight; + // // ToolsUI // this.AllowDrop = true; this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; this.ClientSize = new System.Drawing.Size(324, 362); + this.Controls.Add(this.L_Padding); + this.Controls.Add(this.NUD_Padding); this.Controls.Add(this.pBar1); this.Controls.Add(this.CHK_Delete); this.Controls.Add(this.B_Reset); @@ -171,6 +203,7 @@ private void InitializeComponent() this.Text = "Tools"; this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.closeForm); ((System.ComponentModel.ISupportInitialize)(this.PB_BCLIM)).EndInit(); + ((System.ComponentModel.ISupportInitialize)(this.NUD_Padding)).EndInit(); this.ResumeLayout(false); this.PerformLayout(); @@ -189,5 +222,7 @@ private void InitializeComponent() private System.Windows.Forms.Button B_Reset; private System.Windows.Forms.CheckBox CHK_Delete; private System.Windows.Forms.ProgressBar pBar1; + private System.Windows.Forms.NumericUpDown NUD_Padding; + private System.Windows.Forms.Label L_Padding; } } \ No newline at end of file diff --git a/pk3DS/Tools/ToolsUI.cs b/pk3DS/Tools/ToolsUI.cs index ee5f709..4171680 100644 --- a/pk3DS/Tools/ToolsUI.cs +++ b/pk3DS/Tools/ToolsUI.cs @@ -153,7 +153,6 @@ private void saveARC(string path) { if (!Directory.Exists(path)) { Util.Error("Input path is not a Folder", path); return; } string folderName = Path.GetFileName(path); - if (folderName == null) return; string parentName = Directory.GetParent(path).FullName; int type = CB_Repack.SelectedIndex; switch (type) @@ -179,10 +178,14 @@ private void saveARC(string path) return; var version = dr == DialogResult.Yes ? CTR.GARC.VER_6 : CTR.GARC.VER_4; + int padding = (int)NUD_Padding.Value; + if (version == CTR.GARC.VER_4) + padding = 4; + + string outfolder = Directory.GetParent(path).FullName; new Thread(() => { - string outfolder = Directory.GetParent(path).FullName; - bool r = CTR.GARC.garcPackMS(path, Path.Combine(outfolder, folderName + ".garc"), version, pBar1); + bool r = CTR.GARC.garcPackMS(path, Path.Combine(outfolder, folderName + ".garc"), version, padding, pBar1); if (!r) { Util.Alert("Packing failed."); return; } // Delete path after repacking if (CHK_Delete.Checked && Directory.Exists(path))