├── .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 |
--------------------------------------------------------------------------------