comparison Class1.cs @ 0:85ff734c7605 default tip

Initial commit under GPLv3
author IBBoard <dev@ibboard.co.uk>
date Sat, 06 Oct 2018 20:29:36 +0100
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:85ff734c7605
1 // This file is a part of the SGA Extractor command-line app and is copyright 2006-2018 IBBoard.
2 //
3 // The file and the library/program it is in are licensed under the GNU GPL license, either version 3 of the License or (at your option) any later version. Please see COPYING for more information and the full license.
4 using System;
5 using System.IO;
6 using System.Collections;
7 using System.Text.RegularExpressions;
8 using IBBoard.Relic.RelicTools;
9
10 namespace SgaExtractor
11 {
12 /// <summary>
13 /// Summary description for Class1.
14 /// </summary>
15 class Class1
16 {
17 private const int DEFAULT_MODE = 2;
18 private static readonly string nl = System.Environment.NewLine;
19
20 private static string dest;
21 private static SgaArchive archive;
22 private static ArrayList paths;
23 private static bool overwrite = false;
24 private static bool recursive = false;
25 private static string ext;
26 private static short mode = DEFAULT_MODE;
27 private static string logPath;
28 private static StreamWriter sw;
29 private static bool fullExtract = false;
30
31 /// <summary>
32 /// The main entry point for the application.
33 /// </summary>
34 [STAThread]
35 static void Main(string[] args)
36 {
37 if (args.Length<1 || args[0]=="/?")
38 {
39 ShowHelp("");
40
41 if (args.Length>1 && args[1].ToLower() == "usage")
42 {
43 Output(nl+"Click ENTER to see example usage...");
44 Console.ReadLine();
45 ShowUsage();
46 }
47
48 return;
49 }
50
51 if (!Regex.IsMatch(args[0], "[a-zA-Z0-9]+.sga$"))
52 {
53 ShowHelp("Archive to extract must be specified as the first parameter");
54 return;
55 }
56 else
57 {
58 try
59 {
60 archive = new SgaArchive(args[0]);
61 }
62 catch (FileNotFoundException)
63 {
64 ShowHelp("The archive specified was not found");
65 return;
66 }
67 }
68
69 int i = 1;
70
71 if (args.Length>1)
72 {
73 if (!args[i].StartsWith("-"))
74 {
75 //full-archive extraction with a destination
76 fullExtract = true;
77 dest = args[i];
78 i++;
79 }
80 else if (args[i]=="-paths")
81 {
82 fullExtract = false;
83 i++;
84 paths = new ArrayList();
85
86 while (i<args.Length && !args[i].StartsWith("-"))
87 {
88 paths.Add(args[i]);
89 i++;
90 }
91 }
92 else
93 {
94 fullExtract = true;
95 }
96 }
97 else
98 {
99 fullExtract = true;
100 }
101
102 ArrayList arrFind = new ArrayList();
103 ArrayList arrReplace = new ArrayList();
104
105 while (i<args.Length)
106 {
107 if (!fullExtract && args[i]=="-d" && i<(args.Length-1) && !args[i+1].StartsWith("-"))
108 {
109 if (dest!="" && dest!=null)
110 {
111 ShowHelp("Please only specify one destination - either a single file or a folder");
112 }
113 else
114 {
115 i++;
116
117 dest = args[i];
118 i++;
119 int lastSlash = dest.LastIndexOf('\\');
120
121 if (lastSlash<dest.LastIndexOf('.'))
122 {
123 //it looks like a file, so make sure they've not specified multiple files
124 if (paths == null || paths.Count!=1)
125 {
126 ShowHelp("Multiple files were selected but a single destination file was specified");
127 return;
128 }
129 }
130 }
131 }
132 else if (!fullExtract && args[i]=="-find" && i<(args.Length-3))
133 {
134 i++;
135 while (i<args.Length && !args[i].StartsWith("-"))
136 {
137 arrFind.Add(args[i]);
138 i++;
139 }
140 }
141 else if (!fullExtract && args[i]=="-replace" && i<(args.Length-1))
142 {
143 i++;
144 while (i<args.Length && !args[i].StartsWith("-"))
145 {
146 arrReplace.Add(args[i]);
147 i++;
148 }
149 }
150 else if (!fullExtract && (args[i]=="-extension" || args[i]=="-ext") && i<(args.Length-1))
151 {
152 i++;
153 ext = args[i];
154 i++;
155 }
156 else if (args[i]=="-mode" && i<args.Length-1)
157 {
158 i++;
159 try
160 {
161 mode = short.Parse(args[i]);
162
163 if (mode<1||mode>3)
164 {
165 mode = DEFAULT_MODE;
166 Output("Warning: invalid mode used. Reverting to default");
167 }
168 }
169 catch
170 {
171 mode = DEFAULT_MODE;
172 }
173 i++;
174 }
175 else if (args[i]=="-log" && i<args.Length-1)
176 {
177 i++;
178 logPath = args[i];
179 i++;
180 }
181 else if (args[i]=="--force")
182 {
183 i++;
184 overwrite = true;
185 }
186 else if (!fullExtract && args[i]=="--recursive")
187 {
188 i++;
189 recursive = true;
190 }
191 else
192 {
193 ShowHelp("Invalid parameter: "+args[i]);
194 return;
195 }
196 }
197
198 string [] find;
199 string [] replace;
200
201 if (arrReplace.Count!=arrFind.Count)
202 {
203 ShowHelp("Find and Replace lists must be the same length");
204 return;
205 }
206 else
207 {
208 find = new string[arrFind.Count];
209 replace = new string [arrReplace.Count];
210
211 for (int j = 0; j<arrFind.Count; j++)
212 {
213 find[j] = (string)arrFind[j];
214 }
215
216 for (int j = 0; j<arrReplace.Count; j++)
217 {
218 replace[j] = (string)arrReplace[j];
219 }
220 }
221
222 if ((mode & 1) == 1)
223 {
224 archive.OnExtractFileFail += new SgaArchive.ExtractionNotification(OutputExtractionStatus);
225 archive.OnExtractFolderFail += new SgaArchive.ExtractionNotification(OutputExtractionStatus);
226 }
227
228 if ((mode & 2) == 2)
229 {
230 archive.OnExtractFileSuccess += new SgaArchive.ExtractionNotification(OutputExtractionStatus);
231 archive.OnExtractFolderSuccess += new SgaArchive.ExtractionNotification(OutputExtractionStatus);
232 }
233
234 if (logPath!=null && logPath!="")
235 {
236 sw = new StreamWriter(logPath,true);
237 sw.AutoFlush = true;
238 sw.WriteLine(DateTime.Now.ToString()+" - "+Environment.CommandLine.ToString()+nl);
239 }
240
241 if (paths==null || paths.Count==0)
242 {
243 //full archive extraction
244 if (dest==null || dest == "")
245 {
246 //to default location
247 try
248 {
249 Output("Extracting full archive "+archive.Name+" to default location");
250 archive.ExtractAll(overwrite);
251 Output("Extraction Complete");
252 }
253 catch (FileNotFoundException)
254 {
255 ShowHelp("The archive specified was not found");
256 return;
257 }
258 catch(Exception ex)
259 {
260 Output("Extraction failed: "+ex.Message);
261 }
262 }
263 else
264 {
265 //to specified location
266 try
267 {
268 Output("Extracting full archive "+archive.Name+" to "+dest);
269 archive.ExtractAll(dest, overwrite);
270 Output("Extraction Complete");
271 }
272 catch (FileNotFoundException)
273 {
274 ShowHelp("The archive specified was not found");
275 return;
276 }
277 catch(Exception ex)
278 {
279 Output("Extraction failed: "+ex.Message);
280 }
281 }
282 }
283 else
284 {
285 string path = "";
286
287 for (int j = 0; j<paths.Count; j++)
288 {
289 path = (string)paths[j];
290 int lastindex = path.LastIndexOf('\\');
291
292 if (lastindex>=path.LastIndexOf('.'))
293 {
294 //the path to extract is a folder
295 try
296 {
297 if (ext!=null)
298 {
299 Output("Attempting to extract files of type '"+ext+"' from folder: "+path);
300
301 if (dest==null)
302 {
303 archive.ExtractType(ext, path, find, replace, recursive, overwrite);
304 }
305 else
306 {
307 archive.ExtractType(ext, path, dest, find, replace, recursive, overwrite);
308 }
309 }
310 else
311 {
312 Output("Attempting to extract folder: "+path);
313
314 if (dest==null && find.Length==0)
315 {
316 archive.ExtractFolder(path, recursive, overwrite);
317 }
318 else
319 {
320 archive.ExtractFolder(path, dest, find, replace, recursive, overwrite);
321 }
322 }
323
324 Output("Extraction complete");
325 }
326 catch(Exception ex)
327 {
328 Output("Extraction failed: "+ex.Message);
329 }
330 }
331 else
332 {
333 Output("Attempting to extract file: "+path);
334 try
335 {
336 if (dest==null)
337 {
338 archive.Extract(path, find, replace, overwrite);
339 }
340 else
341 {
342 archive.Extract(path, dest, find, replace, overwrite);
343 }
344 Output("Extraction complete");
345 }
346 catch(Exception ex)
347 {
348 Output("Extraction failed: "+ex.Message);
349 }
350 }
351 }
352 }
353 /*
354 if (args.Length==1 && args[0]=="/?")
355 {
356 ShowHelp("");
357 }
358 else if (args.Length==1 && args[0].EndsWith(".sga"))
359 {
360 try
361 {
362 archive = new SgaArchive(args[0]);
363 Output("Extracting full archive "+args[0]);
364 archive.ExtractAll();
365 Output("Extraction Complete");
366 }
367 catch (RelicSga.Exceptions.FileNotFoundException)
368 {
369 ShowHelp("The archive specified was not found");
370 return;
371 }
372 catch(RelicSga.Exceptions.Exception ex)
373 {
374 Output("Extraction failed: "+ex.Message);
375 }
376 }
377 else if (args.Length ==2 && args[0]=="/?" && args[1]=="usage")
378 {
379 ShowHelp("");
380 Output(nl+"Click ENTER to see example usage...");
381 Console.ReadLine();
382 ShowUsage();
383 }
384 else if (args.Length==2 && args[0].EndsWith(".sga"))
385 {
386 if (args[1]=="--force")
387 {
388 try
389 {
390 archive = new SgaArchive(args[0]);
391 Output("Extracting full archive "+args[0]);
392 archive.ExtractAll(true);
393 Output("Extraction Complete");
394 }
395 catch (RelicSga.Exceptions.FileNotFoundException)
396 {
397 ShowHelp("The archive specified was not found");
398 return;
399 }
400 catch(RelicSga.Exceptions.Exception ex)
401 {
402 Output("Extraction failed: "+ex.Message);
403 }
404 }
405 else if (!args[1].StartsWith("-"))
406 {
407 try
408 {
409 archive = new SgaArchive(args[0]);
410 Output("Extracting full archive "+args[0]+" to "+args[1]);
411 archive.ExtractAll(args[1]);
412 Output("Extraction Complete");
413 }
414 catch (RelicSga.Exceptions.FileNotFoundException)
415 {
416 ShowHelp("The archive specified was not found");
417 return;
418 }
419 catch(RelicSga.Exceptions.Exception ex)
420 {
421 Output("Extraction failed: "+ex.Message);
422 }
423 }
424 else
425 {
426 ShowHelp("Invalid parameter combination");
427 return;
428 }
429 }
430 else if (args.Length<3)
431 {
432 ShowHelp("");
433 }
434 else if (args.Length==3 && args[0].EndsWith(".sga") && !args[1].StartsWith("-") && args[2]=="--force")
435 {
436 try
437 {
438 archive = new SgaArchive(args[0]);
439 Output("Extracting full archive "+args[0]+" to "+args[1]);
440 archive.ExtractAll(args[1], true);
441 Output("Extraction Complete");
442 }
443 catch (RelicSga.Exceptions.FileNotFoundException)
444 {
445 ShowHelp("The archive specified was not found");
446 return;
447 }
448 catch(RelicSga.Exceptions.Exception ex)
449 {
450 Output("Extraction failed: "+ex.Message);
451 }
452 }
453 else
454 {
455 if (!Regex.IsMatch(args[0], "^[a-zA-Z0-9]+.sga$"))
456 {
457 ShowHelp("Archive to extract must be specified as the first parameter");
458 return;
459 }
460 else
461 {
462 try
463 {
464 archive = new SgaArchive(args[0]);
465 }
466 catch (RelicSga.Exceptions.FileNotFoundException)
467 {
468 ShowHelp("The archive specified was not found");
469 return;
470 }
471 }
472
473 if (args[1]!="-paths")
474 {
475 ShowHelp("Must specifie a set of paths to extract");
476 return;
477 }
478
479 int i = 2;
480 paths = new ArrayList();
481
482 while (i<args.Length && !args[i].StartsWith("-"))
483 {
484 paths.Add(args[i]);
485 i++;
486 }
487
488
489 ArrayList arrFind = new ArrayList();
490 ArrayList arrReplace = new ArrayList();
491
492 if (i != args.Length)
493 {
494 while (i<args.Length)
495 {
496 if (args[i]=="-d" && i<(args.Length-1) && !args[i+1].StartsWith("-"))
497 {
498 i++;
499
500 dest = args[i];
501 i++;
502 int lastindex = dest.LastIndexOf('\\');
503
504 if (lastindex<=dest.LastIndexOf('.'))
505 {
506 //it looks like a file, so make sure they've not specified multiple files
507 if (paths.Count!=1)
508 {
509 ShowHelp("Multiple files were selected but a single destination file was specified");
510 return;
511 }
512 }
513 }
514 else if (args[i]=="-find" && i<(args.Length-3))
515 {
516 i++;
517 while (i<args.Length && !args[i].StartsWith("-"))
518 {
519 arrFind.Add(args[i]);
520 i++;
521 }
522 }
523 else if (args[i]=="-replace" && i<(args.Length-1))
524 {
525 i++;
526 while (i<args.Length && !args[i].StartsWith("-"))
527 {
528 arrReplace.Add(args[i]);
529 i++;
530 }
531 }
532 else if ((args[i]=="-extension" || args[i]=="-ext") && i<(args.Length-1))
533 {
534 i++;
535 ext = args[i];
536 i++;
537 }
538 else if (args[i]=="-mode" && i<args.Length-1)
539 {
540 i++;
541 try
542 {
543 mode = short.Parse(args[i]);
544 }
545 catch
546 {
547 mode = DEFAULT_MODE;
548 }
549 i++;
550 }
551 else if (args[i]=="-log" && i<args.Length-1)
552 {
553 i++;
554 logPath = args[i];
555 i++;
556 }
557 else if (args[i]=="--force")
558 {
559 i++;
560 overwrite = true;
561 }
562 else if (args[i]=="--recursive")
563 {
564 i++;
565 recursive = true;
566 }
567 else
568 {
569 ShowHelp("Invalid parameter combination");
570 return;
571 }
572 }
573 }
574
575 string path = "";
576
577 string [] find;
578 string [] replace;
579
580 if (arrReplace.Count!=arrFind.Count)
581 {
582 ShowHelp("Find and Replace lists must be the same length");
583 return;
584 }
585 else
586 {
587 find = new string[arrFind.Count];
588 replace = new string [arrReplace.Count];
589
590 for (int j = 0; j<arrFind.Count; j++)
591 {
592 find[j] = (string)arrFind[j];
593 }
594
595 for (int j = 0; j<arrReplace.Count; j++)
596 {
597 replace[j] = (string)arrReplace[j];
598 }
599 }
600
601 if ((mode & 1) == 1)
602 {
603 archive.OnExtractFileFail += new SgaArchive.ExtractionNotification(OutputExtractionStatus);
604 archive.OnExtractFolderFail += new SgaArchive.ExtractionNotification(OutputExtractionStatus);
605 }
606
607 if ((mode & 2) == 2)
608 {
609 archive.OnExtractFileSuccess += new SgaArchive.ExtractionNotification(OutputExtractionStatus);
610 archive.OnExtractFolderSuccess += new SgaArchive.ExtractionNotification(OutputExtractionStatus);
611 }
612
613 if (logPath!=null && logPath!="")
614 {
615 sw = new StreamWriter(logPath,false);
616 sw.AutoFlush = true;
617 }
618
619 for (int j = 0; j<paths.Count; j++)
620 {
621 path = (string)paths[j];
622 int lastindex = path.LastIndexOf('\\');
623
624 if (lastindex>path.LastIndexOf('.'))
625 {
626 //it's a folder
627 try
628 {
629 if (ext!=null)
630 {
631 Output("Attempting to extract files of type '"+ext+"' from folder: "+path);
632
633 if (dest==null)
634 {
635 archive.ExtractType(ext, path, find, replace, recursive, overwrite);
636 }
637 else
638 {
639 archive.ExtractType(ext, path, dest, find, replace, recursive, overwrite);
640 }
641 }
642 else
643 {
644 Output("Attempting to extract folder: "+path);
645
646 if (dest==null && find.Length==0)
647 {
648 archive.ExtractFolder(path, recursive, overwrite);
649 }
650 else
651 {
652 archive.ExtractFolder(path, dest, find, replace, recursive, overwrite);
653 }
654 }
655
656 Output("Extraction complete");
657 }
658 catch(RelicSga.Exceptions.Exception ex)
659 {
660 Output("Extraction failed: "+ex.Message);
661 }
662 }
663 else
664 {
665 Output("Attempting to extract file: "+path);
666 try
667 {
668 if (dest==null)
669 {
670 archive.Extract(path, find, replace, overwrite);
671 }
672 else
673 {
674 archive.Extract(path, dest, find, replace, overwrite);
675 }
676 Output("Extraction complete");
677 }
678 catch(RelicSga.Exceptions.Exception ex)
679 {
680 Output("Extraction failed: "+ex.Message);
681 }
682 }
683 }
684 }
685 */
686 if (sw!=null)
687 {
688 sw.WriteLine("");
689 sw.Close();
690 }
691 }
692
693 private static void ShowHelp(string error)
694 {
695 if (error!="")
696 {
697
698 error = nl+"Error: "+error+nl;
699
700 if (sw!=null)
701 {
702 //output just the error directly to the log if we errored in some important way and we're logging to a file
703 sw.WriteLine(error);
704 }
705 }
706
707 Output("Extracts files from an SGA archive file, with optional hex-editing"+nl+error+nl+
708 "SGAEXTRACTOR archive -paths path [path [...]] [-d destination]"+nl+
709 " [ -find find[ find ...] -replace repl[ repl] ] [-ext extension] "+nl+
710 " [-mode level] [-log logfile] [--force] [--recursive]"+nl+nl+
711 "SGAEXTRACTOR archive [destination] [-mode level] [-log logfile] [--force]"+nl+nl+
712 " archive\tSpecifies the SGA file to extract from"+nl+
713 " path\t\tSpecifies the file or folder path within the archive"+nl+
714 " \t\t to extract"+nl+
715 " destination\tLocation the files will be extracted to. Defaults to the"+nl+
716 " \t\t location of the archive"+nl+
717 " find\t\tThe text string that will be found to be replace"+nl+
718 " repl\t\tThe text string that will replace the found string"+nl+
719 " extension\tIf a folder is specified in 'path' ext specifies the extension"+nl+
720 " \t\tof file types to extract"+nl+
721 " level\tThe level of feedback required: 0 = minimal feedback, "+nl+
722 " \t\t 1 = failures, 2 = successes, 3 = both (default = 2)"+nl+
723 " log\t\tSpecifies the path of the log file to write to"+nl+
724 " --force\tForces an overwrite of the file or files if they exist"+nl+
725 " --recursive\tIf a folder is specified in 'path', extracts all sub folders",
726 false);
727 }
728
729 private static void ShowUsage()
730 {
731 Output("Example Usage:"+nl+nl+
732 " Extract all of the Dawn of War texture archive to its default location:"+nl+
733 " SGAEXTRACTOR \"c:\\Program Files\\THQ\\Dawn of War\\W40k\\W40KData-SharedTextures-Full.sga\""+nl+nl+
734 " Extract all of the Dawn of War texture archive to a new location:"+nl+
735 " SGAEXTRACTOR \"c:\\Program Files\\THQ\\Dawn of War\\W40k\\W40KData-SharedTextures-Full.sga\" -d c:\\texture_temp"+nl+nl+
736 " Extract all of the Dawn of War RGDs to their default location, overwriting any that already exist:"+nl+nl+
737 " SGAEXTRACTOR \"c:\\Program Files\\THQ\\Dawn of War\\W40k\\W40kData.sga\" -ext .rgd --force"+nl+nl+
738 "MORE...", false);
739 Console.ReadLine();
740 Output(" Extract Chaplain texture from WXP texture archive to its default location:"+nl+
741 " SGAEXTRACTOR \"c:\\Program Files\\THQ\\Dawn of War\\WXP\\WXPData-SharedTextures-Full.sga\" -paths data\\art\\ebps\\races\\space_marines\\texture_share\\sm_chaplain.rsh"+nl+nl+
742 " Extract Chaplain RSH and WTP from WXP texture archive to their default locations:"+nl+
743 " SGAEXTRACTOR \"c:\\Program Files\\THQ\\Dawn of War\\WXP\\WXPData-SharedTextures-Full.sga\" -paths data\\art\\ebps\\races\\space_marines\\texture_share\\sm_chaplain.rsh data\\art\\ebps\\races\\space_marines\\texture_share\\sm_chaplain_default.wtp"+nl+nl+
744 " Extract Chaplain texture from WXP texture archive, save it to a different filename and hex-edit it for a new race:"+nl+
745 " SGAEXTRACTOR \"c:\\Program Files\\THQ\\Dawn of War\\WXP\\WXPData-SharedTextures-Full.sga\" -paths data\\art\\ebps\\races\\space_marines\\texture_share\\sm_chaplain.rsh -d \"c:\\program files\\thq\\dawn of war\\temp\\halved_chaplain.rsh\" -find races/space_marines -replace races/halvedmarines",
746 false);
747 }
748
749 private static void OutputExtractionStatus(string type, string name, string message)
750 {
751 if (message=="")
752 {
753 Output(String.Format("Extraction of {0} \"{1}\" succeeded", type, name));
754 }
755 else
756 {
757 Output(String.Format("Extraction of {0} \"{1}\" failed: {2}", type, name, message));
758 }
759 }
760
761 private static void Output(string str)
762 {
763 Output(str, true);
764 }
765
766 private static void Output(string str, bool log)
767 {
768 if (sw!=null && log)
769 {
770 sw.WriteLine(str);
771 }
772
773 Console.WriteLine(str);
774 }
775 }
776 }