view 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 source

// 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);
		}
	}
}