├── .gitattributes ├── .gitignore ├── GARCTool.sln └── GARCTool ├── GARCTool.cs ├── GARCTool.csproj ├── Interface.Designer.cs ├── Interface.cs ├── Interface.resx ├── Program.cs └── Properties ├── AssemblyInfo.cs ├── Resources.Designer.cs ├── Resources.resx ├── Settings.Designer.cs └── Settings.settings /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.sln.docstates 8 | 9 | # Build results 10 | [Dd]ebug/ 11 | [Dd]ebugPublic/ 12 | [Rr]elease/ 13 | x64/ 14 | build/ 15 | bld/ 16 | [Bb]in/ 17 | [Oo]bj/ 18 | 19 | # MSTest test Results 20 | [Tt]est[Rr]esult*/ 21 | [Bb]uild[Ll]og.* 22 | 23 | #NUNIT 24 | *.VisualState.xml 25 | TestResult.xml 26 | 27 | # Build Results of an ATL Project 28 | [Dd]ebugPS/ 29 | [Rr]eleasePS/ 30 | dlldata.c 31 | 32 | *_i.c 33 | *_p.c 34 | *_i.h 35 | *.ilk 36 | *.meta 37 | *.obj 38 | *.pch 39 | *.pdb 40 | *.pgc 41 | *.pgd 42 | *.rsp 43 | *.sbr 44 | *.tlb 45 | *.tli 46 | *.tlh 47 | *.tmp 48 | *.tmp_proj 49 | *.log 50 | *.vspscc 51 | *.vssscc 52 | .builds 53 | *.pidb 54 | *.svclog 55 | *.scc 56 | 57 | # Chutzpah Test files 58 | _Chutzpah* 59 | 60 | # Visual C++ cache files 61 | ipch/ 62 | *.aps 63 | *.ncb 64 | *.opensdf 65 | *.sdf 66 | *.cachefile 67 | 68 | # Visual Studio profiler 69 | *.psess 70 | *.vsp 71 | *.vspx 72 | 73 | # TFS 2012 Local Workspace 74 | $tf/ 75 | 76 | # Guidance Automation Toolkit 77 | *.gpState 78 | 79 | # ReSharper is a .NET coding add-in 80 | _ReSharper*/ 81 | *.[Rr]e[Ss]harper 82 | *.DotSettings.user 83 | 84 | # JustCode is a .NET coding addin-in 85 | .JustCode 86 | 87 | # TeamCity is a build add-in 88 | _TeamCity* 89 | 90 | # DotCover is a Code Coverage Tool 91 | *.dotCover 92 | 93 | # NCrunch 94 | *.ncrunch* 95 | _NCrunch_* 96 | .*crunch*.local.xml 97 | 98 | # MightyMoose 99 | *.mm.* 100 | AutoTest.Net/ 101 | 102 | # Web workbench (sass) 103 | .sass-cache/ 104 | 105 | # Installshield output folder 106 | [Ee]xpress/ 107 | 108 | # DocProject is a documentation generator add-in 109 | DocProject/buildhelp/ 110 | DocProject/Help/*.HxT 111 | DocProject/Help/*.HxC 112 | DocProject/Help/*.hhc 113 | DocProject/Help/*.hhk 114 | DocProject/Help/*.hhp 115 | DocProject/Help/Html2 116 | DocProject/Help/html 117 | 118 | # Click-Once directory 119 | publish/ 120 | 121 | # Publish Web Output 122 | *.[Pp]ublish.xml 123 | *.azurePubxml 124 | 125 | # NuGet Packages Directory 126 | packages/ 127 | ## TODO: If the tool you use requires repositories.config uncomment the next line 128 | #!packages/repositories.config 129 | 130 | # Enable "build/" folder in the NuGet Packages folder since NuGet packages use it for MSBuild targets 131 | # This line needs to be after the ignore of the build folder (and the packages folder if the line above has been uncommented) 132 | !packages/build/ 133 | 134 | # Windows Azure Build Output 135 | csx/ 136 | *.build.csdef 137 | 138 | # Windows Store app package directory 139 | AppPackages/ 140 | 141 | # Others 142 | sql/ 143 | *.Cache 144 | ClientBin/ 145 | [Ss]tyle[Cc]op.* 146 | ~$* 147 | *~ 148 | *.dbmdl 149 | *.dbproj.schemaview 150 | *.pfx 151 | *.publishsettings 152 | node_modules/ 153 | 154 | # RIA/Silverlight projects 155 | Generated_Code/ 156 | 157 | # Backup & report files from converting an old project file to a newer 158 | # Visual Studio version. Backup files are not needed, because we have git ;-) 159 | _UpgradeReport_Files/ 160 | Backup*/ 161 | UpgradeLog*.XML 162 | UpgradeLog*.htm 163 | 164 | # SQL Server files 165 | *.mdf 166 | *.ldf 167 | 168 | # Business Intelligence projects 169 | *.rdl.data 170 | *.bim.layout 171 | *.bim_*.settings 172 | 173 | # Microsoft Fakes 174 | FakesAssemblies/ 175 | 176 | # ========================= 177 | # Operating System Files 178 | # ========================= 179 | 180 | # OSX 181 | # ========================= 182 | 183 | .DS_Store 184 | .AppleDouble 185 | .LSOverride 186 | 187 | # Icon must ends with two \r. 188 | Icon 189 | 190 | # Thumbnails 191 | ._* 192 | 193 | # Files that might appear on external disk 194 | .Spotlight-V100 195 | .Trashes 196 | 197 | # Windows 198 | # ========================= 199 | 200 | # Windows image file caches 201 | Thumbs.db 202 | ehthumbs.db 203 | 204 | # Folder config file 205 | Desktop.ini 206 | 207 | # Recycle Bin used on file shares 208 | $RECYCLE.BIN/ 209 | 210 | # Windows Installer files 211 | *.cab 212 | *.msi 213 | *.msm 214 | *.msp 215 | -------------------------------------------------------------------------------- /GARCTool.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 11.00 3 | # Visual C# Express 2010 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "GARCTool", "GARCTool\GARCTool.csproj", "{6E5FDA8A-8BAF-4F86-9722-DA3D84D614B9}" 5 | EndProject 6 | Global 7 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 8 | Debug|x86 = Debug|x86 9 | Release|x86 = Release|x86 10 | EndGlobalSection 11 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 12 | {6E5FDA8A-8BAF-4F86-9722-DA3D84D614B9}.Debug|x86.ActiveCfg = Debug|x86 13 | {6E5FDA8A-8BAF-4F86-9722-DA3D84D614B9}.Debug|x86.Build.0 = Debug|x86 14 | {6E5FDA8A-8BAF-4F86-9722-DA3D84D614B9}.Release|x86.ActiveCfg = Release|x86 15 | {6E5FDA8A-8BAF-4F86-9722-DA3D84D614B9}.Release|x86.Build.0 = Release|x86 16 | EndGlobalSection 17 | GlobalSection(SolutionProperties) = preSolution 18 | HideSolutionNode = FALSE 19 | EndGlobalSection 20 | EndGlobal 21 | -------------------------------------------------------------------------------- /GARCTool/GARCTool.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Windows.Forms; 4 | using System.Threading; 5 | 6 | namespace GARCTool // If you are including this source file, replace the namespace with the name of your program. 7 | { 8 | /* Readme 9 | * GARCTool should usually be operated via the 3 main pack/unpack functions within the GARCTool class. 10 | * 11 | * Contains: 12 | * GARCTool : Depends on subsequent classes. Methods are as follows... 13 | * garcOmni - operates pack/unpack on an arbitrary string with preset paths. 14 | * garcPack - Packs up an inFolder path to an outFile garc path, using a temporary working folder. 15 | * garcUnpack - Unpacks an inFile garc path to an outFolder path, with an option to skip decompression (rarely needed). 16 | * 17 | * Util : Utility functions for mundane operations. 18 | * GARC Class and Structures : To unpack GARCs via objects. Packing is done without. 19 | * 20 | * dsdecmp : Tweaked and trimmed from the dsdecmp source code. https://code.google.com/p/dsdecmp 21 | * Requires unsafe code to be allowed, as ptrs are needed. 22 | * LZ11 : (De)Compress methods... 23 | * Decompress(string infile, string outfile) 24 | * Compress(string infile, string outfile) 25 | * Also contained are the subfunctions which operate via MemoryStream, which can be used instead. 26 | * Subclasses: 27 | * Exceptions : to fall back on if (de)compression fails with bad files. 28 | * Supplementary : LZUtil & IOUtils 29 | * 30 | * Code Licensing: 31 | * dsdecmp is licensed under the MIT License. 32 | * The rest of GARCTool is licensed under the MIT License as well. 33 | * http://opensource.org/licenses/MIT 34 | */ 35 | #region GARCTool Classes 36 | public class GARCTool 37 | { 38 | public static bool garcOmni(string path, ProgressBar pBar1 = null) 39 | { 40 | try // Try packing/unpacking 41 | { 42 | if (path.Length == 0 || path == null) 43 | { throw new ArgumentException("Invalid string path"); } 44 | if (Directory.Exists(path)) 45 | garcPack(path, Path.Combine(Directory.GetParent(path).ToString(), Path.GetFileName(path) + ".garc"), Path.Combine(Application.StartupPath, "datapackup"), pBar1); 46 | else if (File.Exists(path)) 47 | garcUnpack(path, path + "_", false, pBar1); 48 | else 49 | { throw new Exception(path + " is not a folder or file"); } 50 | 51 | return true; 52 | } 53 | catch (Exception e) 54 | { 55 | if (pBar1.IsDisposed) 56 | return false; 57 | MessageBox.Show("Error!\n\n" + e.ToString()); 58 | System.Media.SystemSounds.Exclamation.Play(); 59 | return false; 60 | } 61 | } 62 | public static bool garcPack(string folderPath, string garcPath, string buildPath, ProgressBar pBar1 = null) 63 | { 64 | // Check to see if our input folder exists. 65 | if (!new DirectoryInfo(folderPath).Exists) { MessageBox.Show("Folder does not exist."); return false; } 66 | 67 | // Check to see if we can make our buildpath. 68 | if (!new DirectoryInfo(buildPath).Exists) 69 | { 70 | try { Directory.CreateDirectory(buildPath); } 71 | catch { MessageBox.Show(buildPath + " is not a valid directory to work in."); return false; }; 72 | } 73 | 74 | // Okay some basic proofing is done. Proceed. 75 | 76 | // Get the paths of the files to pack up. 77 | string[] filepaths = Directory.GetFiles(folderPath, "*.*", SearchOption.TopDirectoryOnly); 78 | 79 | // Initialize ProgressBar 80 | if (pBar1 == null) pBar1 = new ProgressBar(); 81 | if (pBar1.InvokeRequired) 82 | pBar1.Invoke((MethodInvoker)delegate { 83 | pBar1.Minimum = 0; pBar1.Step = 1; pBar1.Value = 0; pBar1.Maximum = filepaths.Length; 84 | }); 85 | else 86 | pBar1.Minimum = 0; pBar1.Step = 1; pBar1.Value = 0; pBar1.Maximum = filepaths.Length; 87 | 88 | // Copy the files to the working directory so our compression doesn't overwrite anything. 89 | for (int i = 0; i < filepaths.Length; i++) 90 | File.Copy(filepaths[i],Path.Combine(buildPath,Path.GetFileName(filepaths[i])),true); 91 | 92 | // Scan through to see if we have to compress anything. 93 | filepaths = Directory.GetFiles(buildPath, "*.*", SearchOption.TopDirectoryOnly); 94 | for (int i = 0; i < filepaths.Length; i++) 95 | { 96 | int compressed = Path.GetFileName(filepaths[i]).IndexOf("_"); 97 | if (compressed >= 0) 98 | { 99 | // File needs to be compressed and replaced. 100 | string compressedName = Path.Combine(buildPath,Path.GetFileName(filepaths[i]).Substring(compressed+1)); 101 | try 102 | { 103 | dsdecmp.Compress(filepaths[i], compressedName); 104 | try { File.Delete(filepaths[i]); } // Try to delete the decompressed file as we don't write it to the GARC. 105 | catch { MessageBox.Show("Could not delete decompressed file"); } 106 | } 107 | catch 108 | { 109 | try { File.Delete(compressedName); } // Something screwed up. This shouldn't happen unless you're fuzzing... 110 | catch { MessageBox.Show("Could not delete failed compressed file"); } 111 | } 112 | } 113 | } 114 | // Refresh file list 115 | filepaths = Directory.GetFiles(buildPath, "*.*", SearchOption.TopDirectoryOnly); 116 | 117 | using (MemoryStream newGARC = new MemoryStream()) 118 | using (MemoryStream GARCdata = new MemoryStream()) 119 | using (BinaryWriter gw = new BinaryWriter(newGARC)) 120 | { 121 | 122 | // Write GARC header 123 | gw.Write((uint)0x47415243); // Write "CRAG" 124 | gw.Write((uint)0x1C); // Header Length 125 | gw.Write((ushort)0xFEFF); // FEFF BOM 126 | gw.Write((ushort)0x0400); // 127 | gw.Write((uint)0x4); // 128 | gw.Write((uint)0); // Data Offset 129 | gw.Write((uint)0); // File Length 130 | gw.Write((uint)0); // FATB chunk last word 131 | 132 | // Write OTAF 133 | gw.Write((uint)0x4641544F); // OTAF 134 | gw.Write((uint)(0xC + 4 * filepaths.Length)); // Section Size 135 | gw.Write((ushort)filepaths.Length); // Count: n 136 | gw.Write((ushort)0xFFFF); // padding? dunno 137 | 138 | // write BTAF jump offsets 139 | for (int i = 0; i < filepaths.Length; i++) 140 | gw.Write((uint)i * 0x10); 141 | 142 | // Start BTAF 143 | gw.Write((uint)0x46415442); // BTAF 144 | gw.Write((uint)(0xC + 0x10 * filepaths.Length)); // Chunk Size 145 | gw.Write((uint)filepaths.Length); 146 | 147 | uint offset = 0; 148 | uint largest = 0; 149 | for (int i = 0; i < filepaths.Length; i++) 150 | { 151 | FileInfo fi = new FileInfo(filepaths[i]); 152 | using (FileStream fileStream = new FileStream(filepaths[i], FileMode.Open)) 153 | { 154 | gw.Write((uint)1); // garc.btaf.entries[i].bits = br.ReadUInt32(); 155 | gw.Write((uint)offset); // Start/Begin Offset 156 | uint round = (uint)Math.Ceiling(((double)fi.Length / 4)) * 4; 157 | offset += (uint)(round); 158 | gw.Write((uint)offset); // End/Stop Offset 159 | gw.Write((uint)fi.Length); // Length/Size 160 | 161 | if (fi.Length > largest) largest = (uint)fi.Length; 162 | 163 | // Write the data to the BMIF data section 164 | fileStream.CopyTo(GARCdata); // then pad with FF's if not /4 165 | while (GARCdata.Length % 4 > 0) GARCdata.WriteByte(0xFF); 166 | } 167 | if (pBar1.InvokeRequired) 168 | pBar1.Invoke((MethodInvoker)delegate { pBar1.PerformStep(); }); 169 | else 170 | pBar1.PerformStep(); 171 | } 172 | // Roundup Offset 173 | 174 | gw.Write((uint)0x46494D42); 175 | gw.Write((uint)0xC); 176 | gw.Write((uint)offset); 177 | 178 | uint dataOffset = (uint)newGARC.Length; 179 | uint garcLength = (uint)(newGARC.Length + GARCdata.Length); 180 | uint largestFile = (uint)largest; 181 | 182 | gw.Seek(0x10, SeekOrigin.Begin); // Goto the start of the un-set 0 data we set earlier and set it. 183 | gw.Write(dataOffset); // Write Data Offset 184 | gw.Write(garcLength); // Write total GARC Length 185 | gw.Write(largestFile); // Write Largest File stat (?) 186 | 187 | newGARC.Seek(0, SeekOrigin.End); // Goto the end so we can copy the filedata after the GARC headers. 188 | 189 | // Write in the data 190 | GARCdata.Seek(0, SeekOrigin.Begin); // Goto the start so we copy from the start of the filedata. 191 | GARCdata.CopyTo(newGARC); // Copy the data. 192 | 193 | // New File is ready to be saved (memstream newGARC) 194 | try 195 | { 196 | File.Delete(garcPath); // Delete the old garc if it exists, then write our new one 197 | using (FileStream file = new FileStream(garcPath, FileMode.Create, FileAccess.Write)) 198 | newGARC.WriteTo(file); 199 | 200 | // Delete our working directory. 201 | Directory.Delete(buildPath, true); 202 | 203 | // We're done. 204 | System.Media.SystemSounds.Exclamation.Play(); 205 | MessageBox.Show("Pack Successful!\n\n" + filepaths.Length + " files packed to the GARC!"); 206 | return true; 207 | } 208 | catch (Exception e) { MessageBox.Show("Packing Failed!\n\n" + e); return false; } 209 | } 210 | } 211 | public static bool garcUnpack(string garcPath, string outPath, bool skipDecompression, ProgressBar pBar1 = null) 212 | { 213 | if (!File.Exists(garcPath)) { MessageBox.Show("File does not exist"); return false; } 214 | 215 | // Fetch file extension (first 4 bytes) to check if it is a GARC 216 | using (BinaryReader br = new BinaryReader(System.IO.File.OpenRead(garcPath))) 217 | { 218 | try { 219 | string s = new string(Util.Reverse(br.ReadChars(4))); 220 | if (s != "GARC") { MessageBox.Show("Input file is not a .GARC"); return false; } 221 | } 222 | catch (Exception) { MessageBox.Show("Invalid File."); return false; } 223 | } 224 | 225 | // Unpack the GARC 226 | GARC garc = ARC.unpackGARC(garcPath); 227 | 228 | // Get file path infos for exporting 229 | FileInfo fileInfo = new FileInfo(garcPath); 230 | 231 | // Initialize ProgressBar 232 | if (pBar1 == null) pBar1 = new ProgressBar(); 233 | 234 | if (pBar1.InvokeRequired) 235 | pBar1.Invoke((MethodInvoker)delegate 236 | { pBar1.Minimum = 0; pBar1.Step = 1; pBar1.Value = 0; pBar1.Maximum = garc.otaf.nFiles; } 237 | ); 238 | else 239 | { pBar1.Minimum = 0; pBar1.Step = 1; pBar1.Value = 0; pBar1.Maximum = garc.otaf.nFiles; } 240 | 241 | using (BinaryReader br = new BinaryReader(System.IO.File.OpenRead(garcPath))) 242 | { 243 | // Create Extraction folder if it does not exist. 244 | if (!Directory.Exists(outPath)) 245 | { 246 | DirectoryInfo di = Directory.CreateDirectory(outPath); 247 | } 248 | 249 | // Pull out all the files 250 | for (int i = 0; i < garc.otaf.nFiles; i++) 251 | { 252 | string ext = "bin"; 253 | bool compressed = false; 254 | 255 | br.BaseStream.Position = garc.btaf.entries[i].start_offset + garc.data_offset; 256 | try 257 | { 258 | byte lzss = (byte)br.PeekChar(); 259 | 260 | if (lzss == 0x11) 261 | compressed = true; 262 | else 263 | { 264 | ext = Util.GuessExtension(br, "bin"); 265 | br.BaseStream.Seek(0, SeekOrigin.Begin); 266 | } 267 | } 268 | catch { ext = null; } 269 | 270 | // Set File Name 271 | string filename = i.ToString("D" + Math.Ceiling(Math.Log10(garc.otaf.nFiles))); 272 | string fileout = Path.Combine(outPath,filename + "." + ext); 273 | using (BinaryWriter bw = new BinaryWriter(File.OpenWrite(fileout))) 274 | { 275 | // Write out the data for the file 276 | br.BaseStream.Position = garc.btaf.entries[i].start_offset + garc.data_offset; 277 | for (int x = 0; x < garc.btaf.entries[i].length; x++) 278 | { 279 | bw.Write(br.ReadByte()); 280 | } 281 | } 282 | // See if decompression should be attempted. 283 | #region Decompression 284 | if (compressed && !skipDecompression) 285 | { 286 | string decout = Path.Combine(outPath,"dec_" + filename + ".bin"); 287 | try 288 | { 289 | long n = dsdecmp.Decompress(fileout,decout); 290 | try { File.Delete(fileout); } 291 | catch { MessageBox.Show("A compressed file could not be deleted for some reason"); } 292 | 293 | // Try to detect for extension now 294 | using (BinaryReader dr = new BinaryReader(System.IO.File.OpenRead(decout))) 295 | { 296 | ext = Util.GuessExtension(dr, "bin"); 297 | } 298 | 299 | File.Move(decout, Path.Combine(outPath, "dec_" + filename + "." + ext)); 300 | } 301 | catch 302 | { 303 | // File is really not encrypted. 304 | try { File.Delete(decout); } 305 | catch { MessageBox.Show("This shouldn't happen"); } 306 | } 307 | } 308 | if (pBar1.InvokeRequired) 309 | pBar1.Invoke((MethodInvoker)delegate { pBar1.PerformStep(); }); 310 | else 311 | pBar1.PerformStep(); 312 | #endregion 313 | } 314 | } 315 | System.Media.SystemSounds.Exclamation.Play(); 316 | MessageBox.Show("Unpack Successful!\n\n" + garc.otaf.nFiles + " files unpacked from the GARC!"); 317 | return true; 318 | } 319 | } 320 | 321 | // Sub-classes 322 | public class Util 323 | { 324 | public static uint Reverse(uint x) 325 | { 326 | uint y = 0; 327 | for (int i = 0; i < 32; ++i) 328 | { 329 | y <<= 1; 330 | y |= (x & 1); 331 | x >>= 1; 332 | } 333 | return y; 334 | } 335 | public static char[] Reverse(char[] charArray) 336 | { 337 | Array.Reverse(charArray); 338 | return charArray; 339 | } 340 | public static string TrimFromZero(string input) 341 | { 342 | int index = input.IndexOf('\0'); 343 | if (index != input.Length - 1) 344 | return input; 345 | 346 | return input.Substring(0, index); 347 | } 348 | public static string GuessExtension(BinaryReader br, string defaultExt) 349 | { 350 | string ext = ""; 351 | long position = br.BaseStream.Position; 352 | byte[] magic = System.Text.Encoding.ASCII.GetBytes(br.ReadChars(4)); 353 | if (magic[0] < 0x41) return defaultExt; 354 | 355 | // check for extensions 356 | { 357 | // check for 2char container extensions 358 | ushort count = BitConverter.ToUInt16(magic, 2); 359 | br.BaseStream.Position = 4 + 4 * count; 360 | if (br.ReadUInt32() == br.BaseStream.Length) 361 | { 362 | ext += (char)magic[0]; 363 | ext += (char)magic[1]; 364 | } 365 | 366 | // else Assume Generic 367 | else 368 | { 369 | for (int i = 0; i < magic.Length && i < 4; i++) 370 | { 371 | if ((magic[i] >= 'a' && magic[i] <= 'z') || (magic[i] >= 'A' && magic[i] <= 'Z') 372 | || char.IsDigit((char)magic[i])) 373 | { 374 | ext += (char)magic[i]; 375 | } 376 | else 377 | break; 378 | } 379 | } 380 | } 381 | // Return BaseStream position to the start. 382 | br.BaseStream.Position = position; 383 | 384 | if (ext.Length <= 1) 385 | return defaultExt; 386 | return ext; 387 | } 388 | } 389 | #region GARC Class & Struct 390 | public class ARC 391 | { 392 | public static GARC unpackGARC(string path) 393 | { 394 | GARC garc = new GARC(); 395 | BinaryReader br = new BinaryReader(System.IO.File.OpenRead(path)); 396 | 397 | // GARC Header 398 | garc.id = br.ReadChars(4); 399 | garc.header_size = br.ReadUInt32(); 400 | garc.id_endian = br.ReadUInt16(); 401 | if (garc.id_endian == 0xFEFF) 402 | Util.Reverse(garc.id); 403 | garc.constant = br.ReadUInt16(); 404 | garc.file_size = br.ReadUInt32(); 405 | 406 | garc.data_offset = br.ReadUInt32(); 407 | garc.file_length = br.ReadUInt32(); 408 | garc.lastsize = br.ReadUInt32(); 409 | 410 | // OTAF 411 | garc.otaf.id = br.ReadChars(4); 412 | garc.otaf.section_size = br.ReadUInt32(); 413 | garc.otaf.nFiles = br.ReadUInt16(); 414 | garc.otaf.padding = br.ReadUInt16(); 415 | 416 | garc.otaf.entries = new OTAF_Entry[garc.otaf.nFiles]; 417 | // not really needed; plus it's wrong 418 | for (int i = 0; i < garc.otaf.nFiles; i++) 419 | { 420 | uint val = br.ReadUInt32(); 421 | if (garc.otaf.padding == 0xffff) 422 | { 423 | val = Util.Reverse(val); 424 | } 425 | garc.otaf.entries[i].name = val.ToString(); 426 | } 427 | 428 | // BTAF (File Allocation TaBle) 429 | garc.btaf.id = br.ReadChars(4); 430 | garc.btaf.section_size = br.ReadUInt32(); 431 | garc.btaf.nFiles = br.ReadUInt32(); 432 | 433 | garc.btaf.entries = new BTAF_Entry[garc.btaf.nFiles]; 434 | for (int i = 0; i < garc.btaf.nFiles; i++) 435 | { 436 | garc.btaf.entries[i].bits = br.ReadUInt32(); 437 | garc.btaf.entries[i].start_offset = br.ReadUInt32(); 438 | garc.btaf.entries[i].end_offset = br.ReadUInt32(); 439 | garc.btaf.entries[i].length = br.ReadUInt32(); 440 | } 441 | 442 | // BMIF 443 | garc.gmif.id = br.ReadChars(4); 444 | garc.gmif.section_size = br.ReadUInt32(); 445 | garc.gmif.data_size = br.ReadUInt32(); 446 | 447 | // Files data 448 | 449 | br.Close(); 450 | return garc; 451 | } 452 | } 453 | public struct GARC 454 | { 455 | public char[] id; // Always GARC = 0x4E415243 456 | public UInt32 header_size; // Always 0x001C 457 | public UInt16 id_endian; // 0xFFFE 458 | public UInt16 constant; // Always 0x0400 chunk count 459 | public UInt32 file_size; 460 | 461 | public UInt32 data_offset; 462 | public UInt32 file_length; 463 | public UInt32 lastsize; 464 | 465 | public OTAF otaf; 466 | public BTAF btaf; 467 | public GMIF gmif; 468 | } 469 | public struct OTAF 470 | { 471 | public char[] id; 472 | public UInt32 section_size; 473 | public UInt16 nFiles; 474 | public UInt16 padding; 475 | 476 | public OTAF_Entry[] entries; 477 | } 478 | public struct OTAF_Entry 479 | { 480 | public string name; 481 | } 482 | public struct BTAF 483 | { 484 | public char[] id; 485 | public UInt32 section_size; 486 | public UInt32 nFiles; 487 | public BTAF_Entry[] entries; 488 | } 489 | public struct BTAF_Entry 490 | { 491 | public UInt32 bits; 492 | public UInt32 start_offset; 493 | public UInt32 end_offset; 494 | public UInt32 length; 495 | } 496 | public struct GMIF 497 | { 498 | public char[] id; 499 | public UInt32 section_size; 500 | public UInt32 data_size; 501 | } 502 | #endregion 503 | #endregion 504 | #region dsdecmp Classes 505 | public class dsdecmp // LZ11 (de)compression 506 | { 507 | public static long Decompress(string infile, string outfile) 508 | { 509 | // make sure the output directory exists 510 | string outDirectory = Path.GetDirectoryName(outfile); 511 | if (!Directory.Exists(outDirectory)) 512 | Directory.CreateDirectory(outDirectory); 513 | // open the two given files, and delegate to the format-specific code. 514 | using (FileStream inStream = new FileStream(infile, FileMode.Open), 515 | outStream = new FileStream(outfile, FileMode.Create)) 516 | { 517 | return Decompress(inStream, inStream.Length, outStream); 518 | } 519 | } 520 | /// 521 | /// Decompresses the given stream, writing the decompressed data to the given output stream. 522 | /// Assumes Supports(instream) returns true. 523 | /// After this call, the input stream will be positioned at the end of the compressed stream, 524 | /// or at the initial position + inLength, whichever comes first. 525 | /// 526 | /// The stream to decompress. At the end of this method, the position 527 | /// of this stream is directly after the compressed data. 528 | /// The length of the input data. Not necessarily all of the 529 | /// input data may be read (if there is padding, for example), however never more than 530 | /// this number of bytes is read from the input stream. 531 | /// The stream to write the decompressed data to. 532 | /// The length of the output data. 533 | /// When the given length of the input data 534 | /// is not enough to properly decompress the input. 535 | public static long Decompress(Stream instream, long inLength, Stream outstream) 536 | { 537 | #region Format definition in NDSTEK style 538 | /* Data header (32bit) 539 | Bit 0-3 Reserved 540 | Bit 4-7 Compressed type (must be 1 for LZ77) 541 | Bit 8-31 Size of decompressed data. if 0, the next 4 bytes are decompressed length 542 | Repeat below. Each Flag Byte followed by eight Blocks. 543 | Flag data (8bit) 544 | Bit 0-7 Type Flags for next 8 Blocks, MSB first 545 | Block Type 0 - Uncompressed - Copy 1 Byte from Source to Dest 546 | Bit 0-7 One data byte to be copied to dest 547 | Block Type 1 - Compressed - Copy LEN Bytes from Dest-Disp-1 to Dest 548 | If Reserved is 0: - Default 549 | Bit 0-3 Disp MSBs 550 | Bit 4-7 LEN - 3 551 | Bit 8-15 Disp LSBs 552 | If Reserved is 1: - Higher compression rates for files with (lots of) long repetitions 553 | Bit 4-7 Indicator 554 | If Indicator > 1: 555 | Bit 0-3 Disp MSBs 556 | Bit 4-7 LEN - 1 (same bits as Indicator) 557 | Bit 8-15 Disp LSBs 558 | If Indicator is 1: A(B CD E)(F GH) 559 | Bit 0-3 (LEN - 0x111) MSBs 560 | Bit 4-7 Indicator; unused 561 | Bit 8-15 (LEN- 0x111) 'middle'-SBs 562 | Bit 16-19 Disp MSBs 563 | Bit 20-23 (LEN - 0x111) LSBs 564 | Bit 24-31 Disp LSBs 565 | If Indicator is 0: 566 | Bit 0-3 (LEN - 0x11) MSBs 567 | Bit 4-7 Indicator; unused 568 | Bit 8-11 Disp MSBs 569 | Bit 12-15 (LEN - 0x11) LSBs 570 | Bit 16-23 Disp LSBs 571 | */ 572 | #endregion 573 | 574 | long readBytes = 0; 575 | 576 | byte type = (byte)instream.ReadByte(); 577 | if (type != 0x11) 578 | throw new InvalidDataException("The provided stream is not a valid LZ-0x11 " 579 | + "compressed stream (invalid type 0x" + type.ToString("X") + ")"); 580 | byte[] sizeBytes = new byte[3]; 581 | instream.Read(sizeBytes, 0, 3); 582 | int decompressedSize = IOUtils.ToNDSu24(sizeBytes, 0); 583 | readBytes += 4; 584 | if (decompressedSize == 0) 585 | { 586 | sizeBytes = new byte[4]; 587 | instream.Read(sizeBytes, 0, 4); 588 | decompressedSize = IOUtils.ToNDSs32(sizeBytes, 0); 589 | readBytes += 4; 590 | } 591 | 592 | // the maximum 'DISP-1' is still 0xFFF. 593 | int bufferLength = 0x1000; 594 | byte[] buffer = new byte[bufferLength]; 595 | int bufferOffset = 0; 596 | 597 | int currentOutSize = 0; 598 | int flags = 0, mask = 1; 599 | while (currentOutSize < decompressedSize) 600 | { 601 | // (throws when requested new flags byte is not available) 602 | #region Update the mask. If all flag bits have been read, get a new set. 603 | // the current mask is the mask used in the previous run. So if it masks the 604 | // last flag bit, get a new flags byte. 605 | if (mask == 1) 606 | { 607 | if (readBytes >= inLength) 608 | throw new NotEnoughDataException(currentOutSize, decompressedSize); 609 | flags = instream.ReadByte(); readBytes++; 610 | if (flags < 0) 611 | throw new StreamTooShortException(); 612 | mask = 0x80; 613 | } 614 | else 615 | { 616 | mask >>= 1; 617 | } 618 | #endregion 619 | 620 | // bit = 1 <=> compressed. 621 | if ((flags & mask) > 0) 622 | { 623 | // (throws when not enough bytes are available) 624 | #region Get length and displacement('disp') values from next 2, 3 or 4 bytes 625 | 626 | // read the first byte first, which also signals the size of the compressed block 627 | if (readBytes >= inLength) 628 | throw new NotEnoughDataException(currentOutSize, decompressedSize); 629 | int byte1 = instream.ReadByte(); readBytes++; 630 | if (byte1 < 0) 631 | throw new StreamTooShortException(); 632 | 633 | int length = byte1 >> 4; 634 | int disp = -1; 635 | if (length == 0) 636 | { 637 | #region case 0; 0(B C)(D EF) + (0x11)(0x1) = (LEN)(DISP) 638 | 639 | // case 0: 640 | // data = AB CD EF (with A=0) 641 | // LEN = ABC + 0x11 == BC + 0x11 642 | // DISP = DEF + 1 643 | 644 | // we need two more bytes available 645 | if (readBytes + 1 >= inLength) 646 | throw new NotEnoughDataException(currentOutSize, decompressedSize); 647 | int byte2 = instream.ReadByte(); readBytes++; 648 | int byte3 = instream.ReadByte(); readBytes++; 649 | if (byte3 < 0) 650 | throw new StreamTooShortException(); 651 | 652 | length = (((byte1 & 0x0F) << 4) | (byte2 >> 4)) + 0x11; 653 | disp = (((byte2 & 0x0F) << 8) | byte3) + 0x1; 654 | 655 | #endregion 656 | } 657 | else if (length == 1) 658 | { 659 | #region case 1: 1(B CD E)(F GH) + (0x111)(0x1) = (LEN)(DISP) 660 | 661 | // case 1: 662 | // data = AB CD EF GH (with A=1) 663 | // LEN = BCDE + 0x111 664 | // DISP = FGH + 1 665 | 666 | // we need three more bytes available 667 | if (readBytes + 2 >= inLength) 668 | throw new NotEnoughDataException(currentOutSize, decompressedSize); 669 | int byte2 = instream.ReadByte(); readBytes++; 670 | int byte3 = instream.ReadByte(); readBytes++; 671 | int byte4 = instream.ReadByte(); readBytes++; 672 | if (byte4 < 0) 673 | throw new StreamTooShortException(); 674 | 675 | length = (((byte1 & 0x0F) << 12) | (byte2 << 4) | (byte3 >> 4)) + 0x111; 676 | disp = (((byte3 & 0x0F) << 8) | byte4) + 0x1; 677 | 678 | #endregion 679 | } 680 | else 681 | { 682 | #region case > 1: (A)(B CD) + (0x1)(0x1) = (LEN)(DISP) 683 | 684 | // case other: 685 | // data = AB CD 686 | // LEN = A + 1 687 | // DISP = BCD + 1 688 | 689 | // we need only one more byte available 690 | if (readBytes >= inLength) 691 | throw new NotEnoughDataException(currentOutSize, decompressedSize); 692 | int byte2 = instream.ReadByte(); readBytes++; 693 | if (byte2 < 0) 694 | throw new StreamTooShortException(); 695 | 696 | length = ((byte1 & 0xF0) >> 4) + 0x1; 697 | disp = (((byte1 & 0x0F) << 8) | byte2) + 0x1; 698 | 699 | #endregion 700 | } 701 | 702 | if (disp > currentOutSize) 703 | throw new InvalidDataException("Cannot go back more than already written. " 704 | + "DISP = " + disp + ", #written bytes = 0x" + currentOutSize.ToString("X") 705 | + " before 0x" + instream.Position.ToString("X") + " with indicator 0x" 706 | + (byte1 >> 4).ToString("X")); 707 | #endregion 708 | 709 | int bufIdx = bufferOffset + bufferLength - disp; 710 | for (int i = 0; i < length; i++) 711 | { 712 | byte next = buffer[bufIdx % bufferLength]; 713 | bufIdx++; 714 | outstream.WriteByte(next); 715 | buffer[bufferOffset] = next; 716 | bufferOffset = (bufferOffset + 1) % bufferLength; 717 | } 718 | currentOutSize += length; 719 | } 720 | else 721 | { 722 | if (readBytes >= inLength) 723 | throw new NotEnoughDataException(currentOutSize, decompressedSize); 724 | int next = instream.ReadByte(); readBytes++; 725 | if (next < 0) 726 | throw new StreamTooShortException(); 727 | 728 | outstream.WriteByte((byte)next); currentOutSize++; 729 | buffer[bufferOffset] = (byte)next; 730 | bufferOffset = (bufferOffset + 1) % bufferLength; 731 | } 732 | } 733 | 734 | if (readBytes < inLength) 735 | { 736 | // the input may be 4-byte aligned. 737 | if ((readBytes ^ (readBytes & 3)) + 4 < inLength) 738 | throw new TooMuchInputException(readBytes, inLength); 739 | } 740 | 741 | return decompressedSize; 742 | } 743 | 744 | public static int Compress(string infile, string outfile) 745 | { 746 | // make sure the output directory exists 747 | string outDirectory = Path.GetDirectoryName(outfile); 748 | if (!Directory.Exists(outDirectory)) 749 | Directory.CreateDirectory(outDirectory); 750 | // open the proper Streams, and delegate to the format-specific code. 751 | using (FileStream inStream = File.Open(infile, FileMode.Open), 752 | outStream = File.Create(outfile)) 753 | { 754 | return dsdecmp.Compress(inStream, inStream.Length, outStream, true); 755 | } 756 | } 757 | #region Original compression method 758 | /// 759 | /// Compresses the input using the 'original', unoptimized compression algorithm. 760 | /// This algorithm should yield files that are the same as those found in the games. 761 | /// (delegates to the optimized method if LookAhead is set) 762 | /// 763 | public unsafe static int Compress(Stream instream, long inLength, Stream outstream, bool original) 764 | { 765 | // make sure the decompressed size fits in 3 bytes. 766 | // There should be room for four bytes, however I'm not 100% sure if that can be used 767 | // in every game, as it may not be a built-in function. 768 | if (inLength > 0xFFFFFF) 769 | throw new InputTooLargeException(); 770 | 771 | // use the other method if lookahead is enabled 772 | if (!original) 773 | { 774 | return CompressWithLA(instream, inLength, outstream); 775 | } 776 | 777 | // save the input data in an array to prevent having to go back and forth in a file 778 | byte[] indata = new byte[inLength]; 779 | int numReadBytes = instream.Read(indata, 0, (int)inLength); 780 | if (numReadBytes != inLength) 781 | throw new StreamTooShortException(); 782 | 783 | // write the compression header first 784 | outstream.WriteByte(0x11); 785 | outstream.WriteByte((byte)(inLength & 0xFF)); 786 | outstream.WriteByte((byte)((inLength >> 8) & 0xFF)); 787 | outstream.WriteByte((byte)((inLength >> 16) & 0xFF)); 788 | 789 | int compressedLength = 4; 790 | 791 | fixed (byte* instart = &indata[0]) 792 | { 793 | // we do need to buffer the output, as the first byte indicates which blocks are compressed. 794 | // this version does not use a look-ahead, so we do not need to buffer more than 8 blocks at a time. 795 | // (a block is at most 4 bytes long) 796 | byte[] outbuffer = new byte[8 * 4 + 1]; 797 | outbuffer[0] = 0; 798 | int bufferlength = 1, bufferedBlocks = 0; 799 | int readBytes = 0; 800 | while (readBytes < inLength) 801 | { 802 | #region If 8 blocks are bufferd, write them and reset the buffer 803 | // we can only buffer 8 blocks at a time. 804 | if (bufferedBlocks == 8) 805 | { 806 | outstream.Write(outbuffer, 0, bufferlength); 807 | compressedLength += bufferlength; 808 | // reset the buffer 809 | outbuffer[0] = 0; 810 | bufferlength = 1; 811 | bufferedBlocks = 0; 812 | } 813 | #endregion 814 | 815 | // determine if we're dealing with a compressed or raw block. 816 | // it is a compressed block when the next 3 or more bytes can be copied from 817 | // somewhere in the set of already compressed bytes. 818 | int disp; 819 | int oldLength = Math.Min(readBytes, 0x1000); 820 | int length = LZUtil.GetOccurrenceLength(instart + readBytes, (int)Math.Min(inLength - readBytes, 0x10110), 821 | instart + readBytes - oldLength, oldLength, out disp); 822 | 823 | // length not 3 or more? next byte is raw data 824 | if (length < 3) 825 | { 826 | outbuffer[bufferlength++] = *(instart + (readBytes++)); 827 | } 828 | else 829 | { 830 | // 3 or more bytes can be copied? next (length) bytes will be compressed into 2 bytes 831 | readBytes += length; 832 | 833 | // mark the next block as compressed 834 | outbuffer[0] |= (byte)(1 << (7 - bufferedBlocks)); 835 | 836 | if (length > 0x110) 837 | { 838 | // case 1: 1(B CD E)(F GH) + (0x111)(0x1) = (LEN)(DISP) 839 | outbuffer[bufferlength] = 0x10; 840 | outbuffer[bufferlength] |= (byte)(((length - 0x111) >> 12) & 0x0F); 841 | bufferlength++; 842 | outbuffer[bufferlength] = (byte)(((length - 0x111) >> 4) & 0xFF); 843 | bufferlength++; 844 | outbuffer[bufferlength] = (byte)(((length - 0x111) << 4) & 0xF0); 845 | } 846 | else if (length > 0x10) 847 | { 848 | // case 0; 0(B C)(D EF) + (0x11)(0x1) = (LEN)(DISP) 849 | outbuffer[bufferlength] = 0x00; 850 | outbuffer[bufferlength] |= (byte)(((length - 0x111) >> 4) & 0x0F); 851 | bufferlength++; 852 | outbuffer[bufferlength] = (byte)(((length - 0x111) << 4) & 0xF0); 853 | } 854 | else 855 | { 856 | // case > 1: (A)(B CD) + (0x1)(0x1) = (LEN)(DISP) 857 | outbuffer[bufferlength] = (byte)(((length - 1) << 4) & 0xF0); 858 | } 859 | // the last 1.5 bytes are always the disp 860 | outbuffer[bufferlength] |= (byte)(((disp - 1) >> 8) & 0x0F); 861 | bufferlength++; 862 | outbuffer[bufferlength] = (byte)((disp - 1) & 0xFF); 863 | bufferlength++; 864 | } 865 | bufferedBlocks++; 866 | } 867 | 868 | // copy the remaining blocks to the output 869 | if (bufferedBlocks > 0) 870 | { 871 | outstream.Write(outbuffer, 0, bufferlength); 872 | compressedLength += bufferlength; 873 | /*/ make the compressed file 4-byte aligned. 874 | while ((compressedLength % 4) != 0) 875 | { 876 | outstream.WriteByte(0); 877 | compressedLength++; 878 | }/**/ 879 | } 880 | } 881 | 882 | return compressedLength; 883 | } 884 | #region Dynamic Programming compression method 885 | /// 886 | /// Variation of the original compression method, making use of Dynamic Programming to 'look ahead' 887 | /// and determine the optimal 'length' values for the compressed blocks. Is not 100% optimal, 888 | /// as the flag-bytes are not taken into account. 889 | /// 890 | private unsafe static int CompressWithLA(Stream instream, long inLength, Stream outstream) 891 | { 892 | // save the input data in an array to prevent having to go back and forth in a file 893 | byte[] indata = new byte[inLength]; 894 | int numReadBytes = instream.Read(indata, 0, (int)inLength); 895 | if (numReadBytes != inLength) 896 | throw new StreamTooShortException(); 897 | 898 | // write the compression header first 899 | outstream.WriteByte(0x11); 900 | outstream.WriteByte((byte)(inLength & 0xFF)); 901 | outstream.WriteByte((byte)((inLength >> 8) & 0xFF)); 902 | outstream.WriteByte((byte)((inLength >> 16) & 0xFF)); 903 | 904 | int compressedLength = 4; 905 | 906 | fixed (byte* instart = &indata[0]) 907 | { 908 | // we do need to buffer the output, as the first byte indicates which blocks are compressed. 909 | // this version does not use a look-ahead, so we do not need to buffer more than 8 blocks at a time. 910 | // blocks are at most 4 bytes long. 911 | byte[] outbuffer = new byte[8 * 4 + 1]; 912 | outbuffer[0] = 0; 913 | int bufferlength = 1, bufferedBlocks = 0; 914 | int readBytes = 0; 915 | 916 | // get the optimal choices for len and disp 917 | int[] lengths, disps; 918 | GetOptimalCompressionLengths(instart, indata.Length, out lengths, out disps); 919 | while (readBytes < inLength) 920 | { 921 | // we can only buffer 8 blocks at a time. 922 | if (bufferedBlocks == 8) 923 | { 924 | outstream.Write(outbuffer, 0, bufferlength); 925 | compressedLength += bufferlength; 926 | // reset the buffer 927 | outbuffer[0] = 0; 928 | bufferlength = 1; 929 | bufferedBlocks = 0; 930 | } 931 | 932 | 933 | if (lengths[readBytes] == 1) 934 | { 935 | outbuffer[bufferlength++] = *(instart + (readBytes++)); 936 | } 937 | else 938 | { 939 | // mark the next block as compressed 940 | outbuffer[0] |= (byte)(1 << (7 - bufferedBlocks)); 941 | 942 | if (lengths[readBytes] > 0x110) 943 | { 944 | // case 1: 1(B CD E)(F GH) + (0x111)(0x1) = (LEN)(DISP) 945 | outbuffer[bufferlength] = 0x10; 946 | outbuffer[bufferlength] |= (byte)(((lengths[readBytes] - 0x111) >> 12) & 0x0F); 947 | bufferlength++; 948 | outbuffer[bufferlength] = (byte)(((lengths[readBytes] - 0x111) >> 4) & 0xFF); 949 | bufferlength++; 950 | outbuffer[bufferlength] = (byte)(((lengths[readBytes] - 0x111) << 4) & 0xF0); 951 | } 952 | else if (lengths[readBytes] > 0x10) 953 | { 954 | // case 0; 0(B C)(D EF) + (0x11)(0x1) = (LEN)(DISP) 955 | outbuffer[bufferlength] = 0x00; 956 | outbuffer[bufferlength] |= (byte)(((lengths[readBytes] - 0x111) >> 4) & 0x0F); 957 | bufferlength++; 958 | outbuffer[bufferlength] = (byte)(((lengths[readBytes] - 0x111) << 4) & 0xF0); 959 | } 960 | else 961 | { 962 | // case > 1: (A)(B CD) + (0x1)(0x1) = (LEN)(DISP) 963 | outbuffer[bufferlength] = (byte)(((lengths[readBytes] - 1) << 4) & 0xF0); 964 | } 965 | // the last 1.5 bytes are always the disp 966 | outbuffer[bufferlength] |= (byte)(((disps[readBytes] - 1) >> 8) & 0x0F); 967 | bufferlength++; 968 | outbuffer[bufferlength] = (byte)((disps[readBytes] - 1) & 0xFF); 969 | bufferlength++; 970 | 971 | readBytes += lengths[readBytes]; 972 | } 973 | 974 | 975 | bufferedBlocks++; 976 | } 977 | 978 | // copy the remaining blocks to the output 979 | if (bufferedBlocks > 0) 980 | { 981 | outstream.Write(outbuffer, 0, bufferlength); 982 | compressedLength += bufferlength; 983 | /*/ make the compressed file 4-byte aligned. 984 | while ((compressedLength % 4) != 0) 985 | { 986 | outstream.WriteByte(0); 987 | compressedLength++; 988 | }/**/ 989 | } 990 | } 991 | 992 | return compressedLength; 993 | } 994 | #endregion 995 | 996 | #region DP compression helper method; GetOptimalCompressionLengths 997 | /// 998 | /// Gets the optimal compression lengths for each start of a compressed block using Dynamic Programming. 999 | /// This takes O(n^2) time, although in practice it will often be O(n^3) since one of the constants is 0x10110 1000 | /// (the maximum length of a compressed block) 1001 | /// 1002 | /// The data to compress. 1003 | /// The length of the data to compress. 1004 | /// The optimal 'length' of the compressed blocks. For each byte in the input data, 1005 | /// this value is the optimal 'length' value. If it is 1, the block should not be compressed. 1006 | /// The 'disp' values of the compressed blocks. May be 0, in which case the 1007 | /// corresponding length will never be anything other than 1. 1008 | private unsafe static void GetOptimalCompressionLengths(byte* indata, int inLength, out int[] lengths, out int[] disps) 1009 | { 1010 | lengths = new int[inLength]; 1011 | disps = new int[inLength]; 1012 | int[] minLengths = new int[inLength]; 1013 | 1014 | for (int i = inLength - 1; i >= 0; i--) 1015 | { 1016 | // first get the compression length when the next byte is not compressed 1017 | minLengths[i] = int.MaxValue; 1018 | lengths[i] = 1; 1019 | if (i + 1 >= inLength) 1020 | minLengths[i] = 1; 1021 | else 1022 | minLengths[i] = 1 + minLengths[i + 1]; 1023 | // then the optimal compressed length 1024 | int oldLength = Math.Min(0x1000, i); 1025 | // get the appropriate disp while at it. Takes at most O(n) time if oldLength is considered O(n) and 0x10110 constant. 1026 | // however since a lot of files will not be larger than 0x10110, this will often take ~O(n^2) time. 1027 | // be sure to bound the input length with 0x10110, as that's the maximum length for LZ-11 compressed blocks. 1028 | int maxLen = LZUtil.GetOccurrenceLength(indata + i, Math.Min(inLength - i, 0x10110), 1029 | indata + i - oldLength, oldLength, out disps[i]); 1030 | if (disps[i] > i) 1031 | throw new Exception("disp is too large"); 1032 | for (int j = 3; j <= maxLen; j++) 1033 | { 1034 | int blocklen; 1035 | if (j > 0x110) 1036 | blocklen = 4; 1037 | else if (j > 0x10) 1038 | blocklen = 3; 1039 | else 1040 | blocklen = 2; 1041 | int newCompLen; 1042 | if (i + j >= inLength) 1043 | newCompLen = blocklen; 1044 | else 1045 | newCompLen = blocklen + minLengths[i + j]; 1046 | if (newCompLen < minLengths[i]) 1047 | { 1048 | lengths[i] = j; 1049 | minLengths[i] = newCompLen; 1050 | } 1051 | } 1052 | } 1053 | 1054 | // we could optimize this further to also optimize it with regard to the flag-bytes, but that would require 8 times 1055 | // more space and time (one for each position in the block) for only a potentially tiny increase in compression ratio. 1056 | } 1057 | #endregion 1058 | #endregion 1059 | } 1060 | #region Exceptions 1061 | /// 1062 | /// An exception indicating that the file cannot be compressed, because the decompressed size 1063 | /// cannot be represented in the current compression format. 1064 | /// 1065 | public class InputTooLargeException : Exception 1066 | { 1067 | /// 1068 | /// Creates a new exception that indicates that the input is too big to be compressed. 1069 | /// 1070 | public InputTooLargeException() 1071 | : base("The compression ratio is not high enough to fit the input " 1072 | + "in a single compressed file.") { } 1073 | } 1074 | /// 1075 | /// An exception that is thrown by the decompression functions when there 1076 | /// is not enough data available in order to properly decompress the input. 1077 | /// 1078 | public class NotEnoughDataException : IOException 1079 | { 1080 | private long currentOutSize; 1081 | private long totalOutSize; 1082 | /// 1083 | /// Gets the actual number of written bytes. 1084 | /// 1085 | public long WrittenLength { get { return this.currentOutSize; } } 1086 | /// 1087 | /// Gets the number of bytes that was supposed to be written. 1088 | /// 1089 | public long DesiredLength { get { return this.totalOutSize; } } 1090 | 1091 | /// 1092 | /// Creates a new NotEnoughDataException. 1093 | /// 1094 | /// The actual number of written bytes. 1095 | /// The desired number of written bytes. 1096 | public NotEnoughDataException(long currentOutSize, long totalOutSize) 1097 | : base("Not enough data availble; 0x" + currentOutSize.ToString("X") 1098 | + " of " + (totalOutSize < 0 ? "???" : ("0x" + totalOutSize.ToString("X"))) 1099 | + " bytes written.") 1100 | { 1101 | this.currentOutSize = currentOutSize; 1102 | this.totalOutSize = totalOutSize; 1103 | } 1104 | } 1105 | /// 1106 | /// An exception thrown by the compression or decompression function, indicating that the 1107 | /// given input length was too large for the given input stream. 1108 | /// 1109 | public class StreamTooShortException : EndOfStreamException 1110 | { 1111 | /// 1112 | /// Creates a new exception that indicates that the stream was shorter than the given input length. 1113 | /// 1114 | public StreamTooShortException() 1115 | : base("The end of the stream was reached " 1116 | + "before the given amout of data was read.") 1117 | { } 1118 | } 1119 | /// 1120 | /// An exception indication that the input has more data than required in order 1121 | /// to decompress it. This may indicate that more sub-files are present in the file. 1122 | /// 1123 | public class TooMuchInputException : Exception 1124 | { 1125 | /// 1126 | /// Gets the number of bytes read by the decompressed to decompress the stream. 1127 | /// 1128 | public long ReadBytes { get; private set; } 1129 | 1130 | /// 1131 | /// Creates a new exception indicating that the input has more data than necessary for 1132 | /// decompressing th stream. It may indicate that other data is present after the compressed 1133 | /// stream. 1134 | /// 1135 | /// The number of bytes read by the decompressor. 1136 | /// The indicated length of the input stream. 1137 | public TooMuchInputException(long readBytes, long totLength) 1138 | : base("The input contains more data than necessary. Only used 0x" 1139 | + readBytes.ToString("X") + " of 0x" + totLength.ToString("X") + " bytes") 1140 | { 1141 | this.ReadBytes = readBytes; 1142 | } 1143 | } 1144 | #endregion 1145 | #region Supplementary 1146 | /// 1147 | /// Class for I/O-related utility methods. 1148 | /// 1149 | public static class LZUtil 1150 | { 1151 | /// 1152 | /// Determine the maximum size of a LZ-compressed block starting at newPtr, using the already compressed data 1153 | /// starting at oldPtr. Takes O(inLength * oldLength) = O(n^2) time. 1154 | /// 1155 | /// The start of the data that needs to be compressed. 1156 | /// The number of bytes that still need to be compressed. 1157 | /// (or: the maximum number of bytes that _may_ be compressed into one block) 1158 | /// The start of the raw file. 1159 | /// The number of bytes already compressed. 1160 | /// The offset of the start of the longest block to refer to. 1161 | /// The minimum allowed value for 'disp'. 1162 | /// The length of the longest sequence of bytes that can be copied from the already decompressed data. 1163 | public static unsafe int GetOccurrenceLength(byte* newPtr, int newLength, byte* oldPtr, int oldLength, out int disp, int minDisp = 1) 1164 | { 1165 | disp = 0; 1166 | if (newLength == 0) 1167 | return 0; 1168 | int maxLength = 0; 1169 | // try every possible 'disp' value (disp = oldLength - i) 1170 | for (int i = 0; i < oldLength - minDisp; i++) 1171 | { 1172 | // work from the start of the old data to the end, to mimic the original implementation's behaviour 1173 | // (and going from start to end or from end to start does not influence the compression ratio anyway) 1174 | byte* currentOldStart = oldPtr + i; 1175 | int currentLength = 0; 1176 | // determine the length we can copy if we go back (oldLength - i) bytes 1177 | // always check the next 'newLength' bytes, and not just the available 'old' bytes, 1178 | // as the copied data can also originate from what we're currently trying to compress. 1179 | for (int j = 0; j < newLength; j++) 1180 | { 1181 | // stop when the bytes are no longer the same 1182 | if (*(currentOldStart + j) != *(newPtr + j)) 1183 | break; 1184 | currentLength++; 1185 | } 1186 | 1187 | // update the optimal value 1188 | if (currentLength > maxLength) 1189 | { 1190 | maxLength = currentLength; 1191 | disp = oldLength - i; 1192 | 1193 | // if we cannot do better anyway, stop trying. 1194 | if (maxLength == newLength) 1195 | break; 1196 | } 1197 | } 1198 | return maxLength; 1199 | } 1200 | } 1201 | public static class IOUtils 1202 | { 1203 | #region byte[] <-> (u)int 1204 | /// 1205 | /// Returns a 4-byte unsigned integer as used on the NDS converted from four bytes 1206 | /// at a specified position in a byte array. 1207 | /// 1208 | /// The source of the data. 1209 | /// The location of the data in the source. 1210 | /// The indicated 4 bytes converted to uint 1211 | public static uint ToNDSu32(byte[] buffer, int offset) 1212 | { 1213 | return (uint)(buffer[offset] 1214 | | (buffer[offset + 1] << 8) 1215 | | (buffer[offset + 2] << 16) 1216 | | (buffer[offset + 3] << 24)); 1217 | } 1218 | 1219 | /// 1220 | /// Returns a 4-byte signed integer as used on the NDS converted from four bytes 1221 | /// at a specified position in a byte array. 1222 | /// 1223 | /// The source of the data. 1224 | /// The location of the data in the source. 1225 | /// The indicated 4 bytes converted to int 1226 | public static int ToNDSs32(byte[] buffer, int offset) 1227 | { 1228 | return (int)(buffer[offset] 1229 | | (buffer[offset + 1] << 8) 1230 | | (buffer[offset + 2] << 16) 1231 | | (buffer[offset + 3] << 24)); 1232 | } 1233 | 1234 | /// 1235 | /// Converts a u32 value into a sequence of bytes that would make ToNDSu32 return 1236 | /// the given input value. 1237 | /// 1238 | public static byte[] FromNDSu32(uint value) 1239 | { 1240 | return new byte[] { 1241 | (byte)(value & 0xFF), 1242 | (byte)((value >> 8) & 0xFF), 1243 | (byte)((value >> 16) & 0xFF), 1244 | (byte)((value >> 24) & 0xFF) 1245 | }; 1246 | } 1247 | 1248 | /// 1249 | /// Returns a 3-byte integer as used in the built-in compression 1250 | /// formats in the DS, convrted from three bytes at a specified position in a byte array, 1251 | /// 1252 | /// The source of the data. 1253 | /// The location of the data in the source. 1254 | /// The indicated 3 bytes converted to an integer. 1255 | public static int ToNDSu24(byte[] buffer, int offset) 1256 | { 1257 | return (int)(buffer[offset] 1258 | | (buffer[offset + 1] << 8) 1259 | | (buffer[offset + 2] << 16)); 1260 | } 1261 | #endregion 1262 | } 1263 | #endregion 1264 | #endregion 1265 | } 1266 | -------------------------------------------------------------------------------- /GARCTool/GARCTool.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | Debug 5 | x86 6 | 8.0.30703 7 | 2.0 8 | {6E5FDA8A-8BAF-4F86-9722-DA3D84D614B9} 9 | WinExe 10 | Properties 11 | GARCTool 12 | GARCTool 13 | v4.0 14 | Client 15 | 512 16 | 17 | 18 | x86 19 | true 20 | full 21 | false 22 | bin\Debug\ 23 | DEBUG;TRACE 24 | prompt 25 | 4 26 | true 27 | 28 | 29 | x86 30 | pdbonly 31 | true 32 | bin\Release\ 33 | TRACE 34 | prompt 35 | 4 36 | true 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | Form 53 | 54 | 55 | Interface.cs 56 | 57 | 58 | 59 | 60 | 61 | Interface.cs 62 | 63 | 64 | ResXFileCodeGenerator 65 | Resources.Designer.cs 66 | Designer 67 | 68 | 69 | True 70 | Resources.resx 71 | True 72 | 73 | 74 | SettingsSingleFileGenerator 75 | Settings.Designer.cs 76 | 77 | 78 | True 79 | Settings.settings 80 | True 81 | 82 | 83 | 84 | 91 | -------------------------------------------------------------------------------- /GARCTool/Interface.Designer.cs: -------------------------------------------------------------------------------- 1 | namespace GARCTool 2 | { 3 | partial class Interface 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | this.B_OpenFolder = new System.Windows.Forms.Button(); 32 | this.B_Process = new System.Windows.Forms.Button(); 33 | this.TB_Path = new System.Windows.Forms.TextBox(); 34 | this.B_OpenFile = new System.Windows.Forms.Button(); 35 | this.progressBar = new System.Windows.Forms.ProgressBar(); 36 | this.SuspendLayout(); 37 | // 38 | // B_OpenFolder 39 | // 40 | this.B_OpenFolder.Location = new System.Drawing.Point(12, 12); 41 | this.B_OpenFolder.Name = "B_OpenFolder"; 42 | this.B_OpenFolder.Size = new System.Drawing.Size(75, 23); 43 | this.B_OpenFolder.TabIndex = 0; 44 | this.B_OpenFolder.Text = "Open Folder"; 45 | this.B_OpenFolder.UseVisualStyleBackColor = true; 46 | this.B_OpenFolder.Click += new System.EventHandler(this.B_Open_Click); 47 | // 48 | // B_Process 49 | // 50 | this.B_Process.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); 51 | this.B_Process.Location = new System.Drawing.Point(196, 12); 52 | this.B_Process.Name = "B_Process"; 53 | this.B_Process.Size = new System.Drawing.Size(75, 23); 54 | this.B_Process.TabIndex = 1; 55 | this.B_Process.Text = "Process"; 56 | this.B_Process.UseVisualStyleBackColor = true; 57 | this.B_Process.Click += new System.EventHandler(this.B_Process_Click); 58 | // 59 | // TB_Path 60 | // 61 | this.TB_Path.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 62 | | System.Windows.Forms.AnchorStyles.Right))); 63 | this.TB_Path.Location = new System.Drawing.Point(12, 41); 64 | this.TB_Path.Name = "TB_Path"; 65 | this.TB_Path.Size = new System.Drawing.Size(259, 20); 66 | this.TB_Path.TabIndex = 2; 67 | // 68 | // B_OpenFile 69 | // 70 | this.B_OpenFile.Location = new System.Drawing.Point(105, 12); 71 | this.B_OpenFile.Name = "B_OpenFile"; 72 | this.B_OpenFile.Size = new System.Drawing.Size(75, 23); 73 | this.B_OpenFile.TabIndex = 3; 74 | this.B_OpenFile.Text = "Open File"; 75 | this.B_OpenFile.UseVisualStyleBackColor = true; 76 | this.B_OpenFile.Click += new System.EventHandler(this.B_OpenFile_Click); 77 | // 78 | // progressBar 79 | // 80 | this.progressBar.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 81 | | System.Windows.Forms.AnchorStyles.Right))); 82 | this.progressBar.Location = new System.Drawing.Point(12, 67); 83 | this.progressBar.Name = "progressBar"; 84 | this.progressBar.Size = new System.Drawing.Size(259, 13); 85 | this.progressBar.TabIndex = 4; 86 | // 87 | // Interface 88 | // 89 | this.AllowDrop = true; 90 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 91 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 92 | this.ClientSize = new System.Drawing.Size(284, 92); 93 | this.Controls.Add(this.progressBar); 94 | this.Controls.Add(this.B_OpenFile); 95 | this.Controls.Add(this.TB_Path); 96 | this.Controls.Add(this.B_Process); 97 | this.Controls.Add(this.B_OpenFolder); 98 | this.MaximizeBox = false; 99 | this.MaximumSize = new System.Drawing.Size(600, 130); 100 | this.MinimizeBox = false; 101 | this.MinimumSize = new System.Drawing.Size(300, 130); 102 | this.Name = "Interface"; 103 | this.Text = "GARCTool"; 104 | this.ResumeLayout(false); 105 | this.PerformLayout(); 106 | 107 | } 108 | 109 | #endregion 110 | 111 | private System.Windows.Forms.Button B_OpenFolder; 112 | private System.Windows.Forms.Button B_Process; 113 | private System.Windows.Forms.TextBox TB_Path; 114 | private System.Windows.Forms.Button B_OpenFile; 115 | private System.Windows.Forms.ProgressBar progressBar; 116 | } 117 | } 118 | 119 | -------------------------------------------------------------------------------- /GARCTool/Interface.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Forms; 3 | using System.Threading; 4 | 5 | namespace GARCTool 6 | { 7 | public partial class Interface : Form 8 | { 9 | public Interface() 10 | { 11 | InitializeComponent(); 12 | this.DragEnter += new DragEventHandler(Form_DragEnter); 13 | this.DragDrop += new DragEventHandler(Form_DragDrop); 14 | } 15 | private void Form_DragEnter(object sender, DragEventArgs e) 16 | { 17 | if (e.Data.GetDataPresent(DataFormats.FileDrop)) e.Effect = DragDropEffects.Copy; 18 | } 19 | private void Form_DragDrop(object sender, DragEventArgs e) 20 | { 21 | string[] files = (string[])e.Data.GetData(DataFormats.FileDrop); 22 | string path = files[0]; // open first D&D 23 | TB_Path.Text = path; 24 | } 25 | 26 | private void B_Open_Click(object sender, EventArgs e) 27 | { 28 | FolderBrowserDialog fbd = new FolderBrowserDialog(); 29 | if (fbd.ShowDialog() == DialogResult.OK) 30 | TB_Path.Text = fbd.SelectedPath; 31 | } 32 | private void B_OpenFile_Click(object sender, EventArgs e) 33 | { 34 | OpenFileDialog ofd = new OpenFileDialog(); 35 | if (ofd.ShowDialog() == DialogResult.OK) 36 | TB_Path.Text = ofd.FileName; 37 | } 38 | private void B_Process_Click(object sender, EventArgs e) 39 | { 40 | if (TB_Path.Text.Length == 0) 41 | { MessageBox.Show("No Path Loaded"); return; } 42 | Thread thread = new Thread(new ThreadStart(mainThread)); 43 | thread.Start(); 44 | } 45 | 46 | private void mainThread() 47 | { 48 | GARCTool.garcOmni(TB_Path.Text, progressBar); 49 | } 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /GARCTool/Interface.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | -------------------------------------------------------------------------------- /GARCTool/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Windows.Forms; 5 | 6 | namespace GARCTool 7 | { 8 | static class Program 9 | { 10 | /// 11 | /// The main entry point for the application. 12 | /// 13 | [STAThread] 14 | static void Main() 15 | { 16 | Application.EnableVisualStyles(); 17 | Application.SetCompatibleTextRenderingDefault(false); 18 | Application.Run(new Interface()); 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /GARCTool/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("GARChive")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("GARChive")] 13 | [assembly: AssemblyCopyright("Copyright © 2014")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("fa5aa512-f4f6-4803-8b1d-54e9e2e9017a")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /GARCTool/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.17929 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace GARCTool.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("GARCTool.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /GARCTool/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 |  2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /GARCTool/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:4.0.30319.17929 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace GARCTool.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /GARCTool/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | --------------------------------------------------------------------------------