Mercurial > repos > RelicTools > SGAExtractor
diff 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 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Class1.cs Sat Oct 06 20:29:36 2018 +0100 @@ -0,0 +1,776 @@ +// This file is a part of the SGA Extractor command-line app and is copyright 2006-2018 IBBoard. +// +// 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. +using System; +using System.IO; +using System.Collections; +using System.Text.RegularExpressions; +using IBBoard.Relic.RelicTools; + +namespace SgaExtractor +{ + /// <summary> + /// Summary description for Class1. + /// </summary> + class Class1 + { + private const int DEFAULT_MODE = 2; + private static readonly string nl = System.Environment.NewLine; + + private static string dest; + private static SgaArchive archive; + private static ArrayList paths; + private static bool overwrite = false; + private static bool recursive = false; + private static string ext; + private static short mode = DEFAULT_MODE; + private static string logPath; + private static StreamWriter sw; + private static bool fullExtract = false; + + /// <summary> + /// The main entry point for the application. + /// </summary> + [STAThread] + static void Main(string[] args) + { + if (args.Length<1 || args[0]=="/?") + { + ShowHelp(""); + + if (args.Length>1 && args[1].ToLower() == "usage") + { + Output(nl+"Click ENTER to see example usage..."); + Console.ReadLine(); + ShowUsage(); + } + + return; + } + + if (!Regex.IsMatch(args[0], "[a-zA-Z0-9]+.sga$")) + { + ShowHelp("Archive to extract must be specified as the first parameter"); + return; + } + else + { + try + { + archive = new SgaArchive(args[0]); + } + catch (FileNotFoundException) + { + ShowHelp("The archive specified was not found"); + return; + } + } + + int i = 1; + + if (args.Length>1) + { + if (!args[i].StartsWith("-")) + { + //full-archive extraction with a destination + fullExtract = true; + dest = args[i]; + i++; + } + else if (args[i]=="-paths") + { + fullExtract = false; + i++; + paths = new ArrayList(); + + while (i<args.Length && !args[i].StartsWith("-")) + { + paths.Add(args[i]); + i++; + } + } + else + { + fullExtract = true; + } + } + else + { + fullExtract = true; + } + + ArrayList arrFind = new ArrayList(); + ArrayList arrReplace = new ArrayList(); + + while (i<args.Length) + { + if (!fullExtract && args[i]=="-d" && i<(args.Length-1) && !args[i+1].StartsWith("-")) + { + if (dest!="" && dest!=null) + { + ShowHelp("Please only specify one destination - either a single file or a folder"); + } + else + { + i++; + + dest = args[i]; + i++; + int lastSlash = dest.LastIndexOf('\\'); + + if (lastSlash<dest.LastIndexOf('.')) + { + //it looks like a file, so make sure they've not specified multiple files + if (paths == null || paths.Count!=1) + { + ShowHelp("Multiple files were selected but a single destination file was specified"); + return; + } + } + } + } + else if (!fullExtract && args[i]=="-find" && i<(args.Length-3)) + { + i++; + while (i<args.Length && !args[i].StartsWith("-")) + { + arrFind.Add(args[i]); + i++; + } + } + else if (!fullExtract && args[i]=="-replace" && i<(args.Length-1)) + { + i++; + while (i<args.Length && !args[i].StartsWith("-")) + { + arrReplace.Add(args[i]); + i++; + } + } + else if (!fullExtract && (args[i]=="-extension" || args[i]=="-ext") && i<(args.Length-1)) + { + i++; + ext = args[i]; + i++; + } + else if (args[i]=="-mode" && i<args.Length-1) + { + i++; + try + { + mode = short.Parse(args[i]); + + if (mode<1||mode>3) + { + mode = DEFAULT_MODE; + Output("Warning: invalid mode used. Reverting to default"); + } + } + catch + { + mode = DEFAULT_MODE; + } + i++; + } + else if (args[i]=="-log" && i<args.Length-1) + { + i++; + logPath = args[i]; + i++; + } + else if (args[i]=="--force") + { + i++; + overwrite = true; + } + else if (!fullExtract && args[i]=="--recursive") + { + i++; + recursive = true; + } + else + { + ShowHelp("Invalid parameter: "+args[i]); + return; + } + } + + string [] find; + string [] replace; + + if (arrReplace.Count!=arrFind.Count) + { + ShowHelp("Find and Replace lists must be the same length"); + return; + } + else + { + find = new string[arrFind.Count]; + replace = new string [arrReplace.Count]; + + for (int j = 0; j<arrFind.Count; j++) + { + find[j] = (string)arrFind[j]; + } + + for (int j = 0; j<arrReplace.Count; j++) + { + replace[j] = (string)arrReplace[j]; + } + } + + if ((mode & 1) == 1) + { + archive.OnExtractFileFail += new SgaArchive.ExtractionNotification(OutputExtractionStatus); + archive.OnExtractFolderFail += new SgaArchive.ExtractionNotification(OutputExtractionStatus); + } + + if ((mode & 2) == 2) + { + archive.OnExtractFileSuccess += new SgaArchive.ExtractionNotification(OutputExtractionStatus); + archive.OnExtractFolderSuccess += new SgaArchive.ExtractionNotification(OutputExtractionStatus); + } + + if (logPath!=null && logPath!="") + { + sw = new StreamWriter(logPath,true); + sw.AutoFlush = true; + sw.WriteLine(DateTime.Now.ToString()+" - "+Environment.CommandLine.ToString()+nl); + } + + if (paths==null || paths.Count==0) + { + //full archive extraction + if (dest==null || dest == "") + { + //to default location + try + { + Output("Extracting full archive "+archive.Name+" to default location"); + archive.ExtractAll(overwrite); + Output("Extraction Complete"); + } + catch (FileNotFoundException) + { + ShowHelp("The archive specified was not found"); + return; + } + catch(Exception ex) + { + Output("Extraction failed: "+ex.Message); + } + } + else + { + //to specified location + try + { + Output("Extracting full archive "+archive.Name+" to "+dest); + archive.ExtractAll(dest, overwrite); + Output("Extraction Complete"); + } + catch (FileNotFoundException) + { + ShowHelp("The archive specified was not found"); + return; + } + catch(Exception ex) + { + Output("Extraction failed: "+ex.Message); + } + } + } + else + { + string path = ""; + + for (int j = 0; j<paths.Count; j++) + { + path = (string)paths[j]; + int lastindex = path.LastIndexOf('\\'); + + if (lastindex>=path.LastIndexOf('.')) + { + //the path to extract is a folder + try + { + if (ext!=null) + { + Output("Attempting to extract files of type '"+ext+"' from folder: "+path); + + if (dest==null) + { + archive.ExtractType(ext, path, find, replace, recursive, overwrite); + } + else + { + archive.ExtractType(ext, path, dest, find, replace, recursive, overwrite); + } + } + else + { + Output("Attempting to extract folder: "+path); + + if (dest==null && find.Length==0) + { + archive.ExtractFolder(path, recursive, overwrite); + } + else + { + archive.ExtractFolder(path, dest, find, replace, recursive, overwrite); + } + } + + Output("Extraction complete"); + } + catch(Exception ex) + { + Output("Extraction failed: "+ex.Message); + } + } + else + { + Output("Attempting to extract file: "+path); + try + { + if (dest==null) + { + archive.Extract(path, find, replace, overwrite); + } + else + { + archive.Extract(path, dest, find, replace, overwrite); + } + Output("Extraction complete"); + } + catch(Exception ex) + { + Output("Extraction failed: "+ex.Message); + } + } + } + } +/* + if (args.Length==1 && args[0]=="/?") + { + ShowHelp(""); + } + else if (args.Length==1 && args[0].EndsWith(".sga")) + { + try + { + archive = new SgaArchive(args[0]); + Output("Extracting full archive "+args[0]); + archive.ExtractAll(); + Output("Extraction Complete"); + } + catch (RelicSga.Exceptions.FileNotFoundException) + { + ShowHelp("The archive specified was not found"); + return; + } + catch(RelicSga.Exceptions.Exception ex) + { + Output("Extraction failed: "+ex.Message); + } + } + else if (args.Length ==2 && args[0]=="/?" && args[1]=="usage") + { + ShowHelp(""); + Output(nl+"Click ENTER to see example usage..."); + Console.ReadLine(); + ShowUsage(); + } + else if (args.Length==2 && args[0].EndsWith(".sga")) + { + if (args[1]=="--force") + { + try + { + archive = new SgaArchive(args[0]); + Output("Extracting full archive "+args[0]); + archive.ExtractAll(true); + Output("Extraction Complete"); + } + catch (RelicSga.Exceptions.FileNotFoundException) + { + ShowHelp("The archive specified was not found"); + return; + } + catch(RelicSga.Exceptions.Exception ex) + { + Output("Extraction failed: "+ex.Message); + } + } + else if (!args[1].StartsWith("-")) + { + try + { + archive = new SgaArchive(args[0]); + Output("Extracting full archive "+args[0]+" to "+args[1]); + archive.ExtractAll(args[1]); + Output("Extraction Complete"); + } + catch (RelicSga.Exceptions.FileNotFoundException) + { + ShowHelp("The archive specified was not found"); + return; + } + catch(RelicSga.Exceptions.Exception ex) + { + Output("Extraction failed: "+ex.Message); + } + } + else + { + ShowHelp("Invalid parameter combination"); + return; + } + } + else if (args.Length<3) + { + ShowHelp(""); + } + else if (args.Length==3 && args[0].EndsWith(".sga") && !args[1].StartsWith("-") && args[2]=="--force") + { + try + { + archive = new SgaArchive(args[0]); + Output("Extracting full archive "+args[0]+" to "+args[1]); + archive.ExtractAll(args[1], true); + Output("Extraction Complete"); + } + catch (RelicSga.Exceptions.FileNotFoundException) + { + ShowHelp("The archive specified was not found"); + return; + } + catch(RelicSga.Exceptions.Exception ex) + { + Output("Extraction failed: "+ex.Message); + } + } + else + { + if (!Regex.IsMatch(args[0], "^[a-zA-Z0-9]+.sga$")) + { + ShowHelp("Archive to extract must be specified as the first parameter"); + return; + } + else + { + try + { + archive = new SgaArchive(args[0]); + } + catch (RelicSga.Exceptions.FileNotFoundException) + { + ShowHelp("The archive specified was not found"); + return; + } + } + + if (args[1]!="-paths") + { + ShowHelp("Must specifie a set of paths to extract"); + return; + } + + int i = 2; + paths = new ArrayList(); + + while (i<args.Length && !args[i].StartsWith("-")) + { + paths.Add(args[i]); + i++; + } + + + ArrayList arrFind = new ArrayList(); + ArrayList arrReplace = new ArrayList(); + + if (i != args.Length) + { + while (i<args.Length) + { + if (args[i]=="-d" && i<(args.Length-1) && !args[i+1].StartsWith("-")) + { + i++; + + dest = args[i]; + i++; + int lastindex = dest.LastIndexOf('\\'); + + if (lastindex<=dest.LastIndexOf('.')) + { + //it looks like a file, so make sure they've not specified multiple files + if (paths.Count!=1) + { + ShowHelp("Multiple files were selected but a single destination file was specified"); + return; + } + } + } + else if (args[i]=="-find" && i<(args.Length-3)) + { + i++; + while (i<args.Length && !args[i].StartsWith("-")) + { + arrFind.Add(args[i]); + i++; + } + } + else if (args[i]=="-replace" && i<(args.Length-1)) + { + i++; + while (i<args.Length && !args[i].StartsWith("-")) + { + arrReplace.Add(args[i]); + i++; + } + } + else if ((args[i]=="-extension" || args[i]=="-ext") && i<(args.Length-1)) + { + i++; + ext = args[i]; + i++; + } + else if (args[i]=="-mode" && i<args.Length-1) + { + i++; + try + { + mode = short.Parse(args[i]); + } + catch + { + mode = DEFAULT_MODE; + } + i++; + } + else if (args[i]=="-log" && i<args.Length-1) + { + i++; + logPath = args[i]; + i++; + } + else if (args[i]=="--force") + { + i++; + overwrite = true; + } + else if (args[i]=="--recursive") + { + i++; + recursive = true; + } + else + { + ShowHelp("Invalid parameter combination"); + return; + } + } + } + + string path = ""; + + string [] find; + string [] replace; + + if (arrReplace.Count!=arrFind.Count) + { + ShowHelp("Find and Replace lists must be the same length"); + return; + } + else + { + find = new string[arrFind.Count]; + replace = new string [arrReplace.Count]; + + for (int j = 0; j<arrFind.Count; j++) + { + find[j] = (string)arrFind[j]; + } + + for (int j = 0; j<arrReplace.Count; j++) + { + replace[j] = (string)arrReplace[j]; + } + } + + if ((mode & 1) == 1) + { + archive.OnExtractFileFail += new SgaArchive.ExtractionNotification(OutputExtractionStatus); + archive.OnExtractFolderFail += new SgaArchive.ExtractionNotification(OutputExtractionStatus); + } + + if ((mode & 2) == 2) + { + archive.OnExtractFileSuccess += new SgaArchive.ExtractionNotification(OutputExtractionStatus); + archive.OnExtractFolderSuccess += new SgaArchive.ExtractionNotification(OutputExtractionStatus); + } + + if (logPath!=null && logPath!="") + { + sw = new StreamWriter(logPath,false); + sw.AutoFlush = true; + } + + for (int j = 0; j<paths.Count; j++) + { + path = (string)paths[j]; + int lastindex = path.LastIndexOf('\\'); + + if (lastindex>path.LastIndexOf('.')) + { + //it's a folder + try + { + if (ext!=null) + { + Output("Attempting to extract files of type '"+ext+"' from folder: "+path); + + if (dest==null) + { + archive.ExtractType(ext, path, find, replace, recursive, overwrite); + } + else + { + archive.ExtractType(ext, path, dest, find, replace, recursive, overwrite); + } + } + else + { + Output("Attempting to extract folder: "+path); + + if (dest==null && find.Length==0) + { + archive.ExtractFolder(path, recursive, overwrite); + } + else + { + archive.ExtractFolder(path, dest, find, replace, recursive, overwrite); + } + } + + Output("Extraction complete"); + } + catch(RelicSga.Exceptions.Exception ex) + { + Output("Extraction failed: "+ex.Message); + } + } + else + { + Output("Attempting to extract file: "+path); + try + { + if (dest==null) + { + archive.Extract(path, find, replace, overwrite); + } + else + { + archive.Extract(path, dest, find, replace, overwrite); + } + Output("Extraction complete"); + } + catch(RelicSga.Exceptions.Exception ex) + { + Output("Extraction failed: "+ex.Message); + } + } + } + } +*/ + if (sw!=null) + { + sw.WriteLine(""); + sw.Close(); + } + } + + private static void ShowHelp(string error) + { + if (error!="") + { + + error = nl+"Error: "+error+nl; + + if (sw!=null) + { + //output just the error directly to the log if we errored in some important way and we're logging to a file + sw.WriteLine(error); + } + } + + Output("Extracts files from an SGA archive file, with optional hex-editing"+nl+error+nl+ + "SGAEXTRACTOR archive -paths path [path [...]] [-d destination]"+nl+ + " [ -find find[ find ...] -replace repl[ repl] ] [-ext extension] "+nl+ + " [-mode level] [-log logfile] [--force] [--recursive]"+nl+nl+ + "SGAEXTRACTOR archive [destination] [-mode level] [-log logfile] [--force]"+nl+nl+ + " archive\tSpecifies the SGA file to extract from"+nl+ + " path\t\tSpecifies the file or folder path within the archive"+nl+ + " \t\t to extract"+nl+ + " destination\tLocation the files will be extracted to. Defaults to the"+nl+ + " \t\t location of the archive"+nl+ + " find\t\tThe text string that will be found to be replace"+nl+ + " repl\t\tThe text string that will replace the found string"+nl+ + " extension\tIf a folder is specified in 'path' ext specifies the extension"+nl+ + " \t\tof file types to extract"+nl+ + " level\tThe level of feedback required: 0 = minimal feedback, "+nl+ + " \t\t 1 = failures, 2 = successes, 3 = both (default = 2)"+nl+ + " log\t\tSpecifies the path of the log file to write to"+nl+ + " --force\tForces an overwrite of the file or files if they exist"+nl+ + " --recursive\tIf a folder is specified in 'path', extracts all sub folders", + false); + } + + private static void ShowUsage() + { + Output("Example Usage:"+nl+nl+ + " Extract all of the Dawn of War texture archive to its default location:"+nl+ + " SGAEXTRACTOR \"c:\\Program Files\\THQ\\Dawn of War\\W40k\\W40KData-SharedTextures-Full.sga\""+nl+nl+ + " Extract all of the Dawn of War texture archive to a new location:"+nl+ + " SGAEXTRACTOR \"c:\\Program Files\\THQ\\Dawn of War\\W40k\\W40KData-SharedTextures-Full.sga\" -d c:\\texture_temp"+nl+nl+ + " Extract all of the Dawn of War RGDs to their default location, overwriting any that already exist:"+nl+nl+ + " SGAEXTRACTOR \"c:\\Program Files\\THQ\\Dawn of War\\W40k\\W40kData.sga\" -ext .rgd --force"+nl+nl+ + "MORE...", false); + Console.ReadLine(); + Output(" Extract Chaplain texture from WXP texture archive to its default location:"+nl+ + " 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+ + " Extract Chaplain RSH and WTP from WXP texture archive to their default locations:"+nl+ + " 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+ + " Extract Chaplain texture from WXP texture archive, save it to a different filename and hex-edit it for a new race:"+nl+ + " 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", + false); + } + + private static void OutputExtractionStatus(string type, string name, string message) + { + if (message=="") + { + Output(String.Format("Extraction of {0} \"{1}\" succeeded", type, name)); + } + else + { + Output(String.Format("Extraction of {0} \"{1}\" failed: {2}", type, name, message)); + } + } + + private static void Output(string str) + { + Output(str, true); + } + + private static void Output(string str, bool log) + { + if (sw!=null && log) + { + sw.WriteLine(str); + } + + Console.WriteLine(str); + } + } +}