using System; using System.IO; using SabreTools.IO.Extensions; using SabreTools.Serialization.Wrappers; namespace ExtractionTool { class Program { static void Main(string[] args) { #if NET462_OR_GREATER || NETCOREAPP // Register the codepages System.Text.Encoding.RegisterProvider(System.Text.CodePagesEncodingProvider.Instance); #endif // Get the options from the arguments var options = Options.ParseOptions(args); // If we have an invalid state if (options == null) { Options.DisplayHelp(); return; } // Loop through the input paths foreach (string inputPath in options.InputPaths) { ExtractPath(inputPath, options.OutputPath, options.Debug); } } /// /// Wrapper to extract data for a single path /// /// File or directory path /// Output directory path /// Enable including debug information private static void ExtractPath(string path, string outputDirectory, bool includeDebug) { // Normalize by getting the full path path = Path.GetFullPath(path); Console.WriteLine($"Checking possible path: {path}"); // Check if the file or directory exists if (File.Exists(path)) { ExtractFile(path, outputDirectory, includeDebug); } else if (Directory.Exists(path)) { foreach (string file in IOExtensions.SafeEnumerateFiles(path, "*", SearchOption.AllDirectories)) { ExtractFile(file, outputDirectory, includeDebug); } } else { Console.WriteLine($"{path} does not exist, skipping..."); } } /// /// Print information for a single file, if possible /// private static void ExtractFile(string file, string outputDirectory, bool includeDebug) { Console.WriteLine($"Attempting to extract all files from {file}"); using Stream stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); // Get the extension for certain checks string extension = Path.GetExtension(file).ToLower().TrimStart('.'); // Get the first 16 bytes for matching byte[] magic = new byte[16]; try { int read = stream.Read(magic, 0, 16); stream.Seek(0, SeekOrigin.Begin); } catch (Exception ex) { if (includeDebug) Console.Error.WriteLine(ex); return; } // TODO: When extractable wrapper types are exposed to this, use them instead of guessing // Get the file type WrapperType ft = WrapperFactory.GetFileType(magic, extension); var wrapper = WrapperFactory.CreateWrapper(ft, stream); // Create the output directory Directory.CreateDirectory(outputDirectory); // 7-zip if (wrapper is SevenZip sz) { Console.WriteLine("Extracting 7-zip contents"); Console.WriteLine(); #if NET20 || NET35 || NET40 || NET452 Console.WriteLine("Extraction is not supported for this framework!"); Console.WriteLine(); #else sz.Extract(outputDirectory, includeDebug); #endif } // BFPK archive else if (wrapper is BFPK bfpk) { Console.WriteLine("Extracting BFPK contents"); Console.WriteLine(); bfpk.Extract(outputDirectory, includeDebug); } // BSP else if (wrapper is BSP bsp) { Console.WriteLine("Extracting BSP contents"); Console.WriteLine(); bsp.Extract(outputDirectory, includeDebug); } // bzip2 else if (wrapper is BZip2 bzip2) { Console.WriteLine("Extracting bzip2 contents"); Console.WriteLine(); bzip2.Extract(outputDirectory, includeDebug); } // CFB else if (wrapper is CFB cfb) { // Build the installer information Console.WriteLine("Extracting CFB contents"); Console.WriteLine(); #if NET20 || NET35 Console.WriteLine("Extraction is not supported for this framework!"); Console.WriteLine(); #else cfb.Extract(outputDirectory, includeDebug); #endif } // Executable else if (ft == WrapperType.Executable) { Console.WriteLine("Extracting executable contents"); Console.WriteLine(); var exe = WrapperFactory.CreateExecutableWrapper(stream); if (exe == null) return; // New Executable if (exe is NewExecutable nex) { // Wise Installer -- Reimplement // var wi = new WiseInstaller(); // if (wi.CheckExecutable(file, nex, includeDebug) != null) // wi.Extract(file, nex, outputDirectory, includeDebug); } // Portable Executable else if (exe is PortableExecutable pex) { // CExe, Embedded Archives, Embedded Executables pex.Extract(outputDirectory, includeDebug); // 7-zip SFX -- Reimplement // var szsfx = new SevenZipSFX(); // if (szsfx.CheckExecutable(file, pex, includeDebug) != null) // szsfx.Extract(file, pex, outputDirectory, includeDebug); // WinRAR SFX -- Reimplement // var wrsfx = new WinRARSFX(); // if (wrsfx.CheckExecutable(file, pex, includeDebug) != null) // wrsfx.Extract(file, pex, outputDirectory, includeDebug); // WinZip SFX -- Reimplement // var wzsfx = new WinZipSFX(); // if (wzsfx.CheckExecutable(file, pex, includeDebug) != null) // wzsfx.Extract(file, pex, outputDirectory, includeDebug); // Wise Installer -- Reimplement // var wi = new WiseInstaller(); // if (wi.CheckExecutable(file, pex, includeDebug) != null) // wi.Extract(file, pex, outputDirectory, includeDebug); } } // GCF else if (wrapper is GCF gcf) { Console.WriteLine("Extracting GCF contents"); Console.WriteLine(); gcf.Extract(outputDirectory, includeDebug); } // gzip else if (wrapper is GZip gzip) { Console.WriteLine("Extracting gzip contents"); Console.WriteLine(); gzip.Extract(outputDirectory, includeDebug); } // InstallShield Archive V3 (Z) else if (wrapper is InstallShieldArchiveV3 isv3) { Console.WriteLine("Extracting InstallShield Archive V3 contents"); Console.WriteLine(); isv3.Extract(outputDirectory, includeDebug); } // IS-CAB archive -- Reimplement // else if (wrapper is InstallShieldCabinet iscab) // { // Console.WriteLine("Extracting IS-CAB contents"); // Console.WriteLine(); // iscab.Extract(outputDirectory, includeDebug); // } // LZ-compressed file, KWAJ variant else if (wrapper is LZKWAJ kwaj) { Console.WriteLine("Extracting LZ-compressed file, KWAJ variant contents"); Console.WriteLine(); kwaj.Extract(outputDirectory, includeDebug); } // LZ-compressed file, QBasic variant else if (wrapper is LZQBasic qbasic) { Console.WriteLine("Extracting LZ-compressed file, QBasic variant contents"); Console.WriteLine(); qbasic.Extract(outputDirectory, includeDebug); } // LZ-compressed file, SZDD variant else if (wrapper is LZSZDD szdd) { Console.WriteLine("Extracting LZ-compressed file, SZDD variant contents"); Console.WriteLine(); szdd.Extract(outputDirectory, includeDebug); } // Microsoft Cabinet archive else if (wrapper is MicrosoftCabinet mscab) { Console.WriteLine("Extracting MS-CAB contents"); Console.WriteLine("WARNING: LZX and Quantum compression schemes are not supported so some files may be skipped!"); Console.WriteLine(); MicrosoftCabinet.ExtractSet(file, outputDirectory, includeDebug); } // MoPaQ (MPQ) archive -- Reimplement // else if (wrapper is MoPaQ mpq) // { // Console.WriteLine("Extracting MoPaQ contents"); // Console.WriteLine(); #if NET20 || NET35 || !(WINX86 || WINX64) // Console.WriteLine("Extraction is not supported for this framework!"); // Console.WriteLine(); #else // mpq.Extract(stream, file, outputDirectory, includeDebug: true); #endif // } // PAK else if (wrapper is PAK pak) { Console.WriteLine("Extracting PAK contents"); Console.WriteLine(); pak.Extract(outputDirectory, includeDebug); } // PFF else if (wrapper is PFF pff) { Console.WriteLine("Extracting PFF contents"); Console.WriteLine(); pff.Extract(outputDirectory, includeDebug); } // PKZIP else if (wrapper is PKZIP pkzip) { Console.WriteLine("Extracting PKZIP contents"); Console.WriteLine(); #if NET20 || NET35 || NET40 || NET452 Console.WriteLine("Extraction is not supported for this framework!"); Console.WriteLine(); #else pkzip.Extract(outputDirectory, includeDebug); #endif } // Quantum else if (wrapper is Quantum quantum) { Console.WriteLine("Extracting Quantum contents"); Console.WriteLine(); quantum.Extract(outputDirectory, includeDebug); } // RAR else if (wrapper is RAR rar) { Console.WriteLine("Extracting RAR contents"); Console.WriteLine(); #if NET20 || NET35 || NET40 || NET452 Console.WriteLine("Extraction is not supported for this framework!"); Console.WriteLine(); #else rar.Extract(outputDirectory, includeDebug); #endif } // SGA else if (wrapper is SGA sga) { Console.WriteLine("Extracting SGA contents"); Console.WriteLine(); sga.Extract(outputDirectory, includeDebug); } // Tape Archive else if (wrapper is TapeArchive tar) { Console.WriteLine("Extracting Tape Archive contents"); Console.WriteLine(); #if NET20 || NET35 || NET40 || NET452 Console.WriteLine("Extraction is not supported for this framework!"); Console.WriteLine(); #else tar.Extract(outputDirectory, includeDebug); #endif } // VBSP else if (wrapper is VBSP vbsp) { Console.WriteLine("Extracting VBSP contents"); Console.WriteLine(); vbsp.Extract(outputDirectory, includeDebug); } // VPK else if (wrapper is VPK vpk) { Console.WriteLine("Extracting VPK contents"); Console.WriteLine(); vpk.Extract(outputDirectory, includeDebug); } // WAD3 else if (wrapper is WAD3 wad) { Console.WriteLine("Extracting WAD3 contents"); Console.WriteLine(); wad.Extract(outputDirectory, includeDebug); } // xz else if (wrapper is XZ xz) { Console.WriteLine("Extracting xz contents"); Console.WriteLine(); #if NET20 || NET35 || NET40 || NET452 Console.WriteLine("Extraction is not supported for this framework!"); Console.WriteLine(); #else xz.Extract(outputDirectory, includeDebug); #endif } // XZP else if (wrapper is XZP xzp) { Console.WriteLine("Extracting XZP contents"); Console.WriteLine(); xzp.Extract(outputDirectory, includeDebug); } // Everything else else { Console.WriteLine("Not a supported extractable file format, skipping..."); Console.WriteLine(); return; } } } }