From 1894376ab6e7dd921ef572ff6d3ed0cc0d83c3de Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Thu, 28 Aug 2025 15:11:21 -0400 Subject: [PATCH] Initial migration of ExtractionTool --- .vscode/launch.json | 14 + ExtractionTool/ExtractionTool.csproj | 71 ++++ ExtractionTool/Options.cs | 129 ++++++ ExtractionTool/Program.cs | 424 ++++++++++++++++++++ SabreTools.Serialization.sln | 48 ++- SabreTools.Serialization/Wrappers/LZSZDD.cs | 4 + SabreTools.Serialization/Wrappers/XZ.cs | 4 +- publish-nix.sh | 71 ++++ publish-win.ps1 | 67 +++- 9 files changed, 826 insertions(+), 6 deletions(-) create mode 100644 ExtractionTool/ExtractionTool.csproj create mode 100644 ExtractionTool/Options.cs create mode 100644 ExtractionTool/Program.cs diff --git a/.vscode/launch.json b/.vscode/launch.json index dd43191a..d9c5e544 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -4,6 +4,20 @@ // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md "version": "0.2.0", "configurations": [ + { + "name": ".NET Core Launch (ExtractionTool)", + "type": "coreclr", + "request": "launch", + "preLaunchTask": "build", + // If you have changed target frameworks, make sure to update the program path. + "program": "${workspaceFolder}/ExtractionTool/bin/Debug/net9.0/ExtractionTool.dll", + "args": [], + "cwd": "${workspaceFolder}", + // For more information about the 'console' field, see https://aka.ms/VSCode-CS-LaunchJson-Console + "console": "internalConsole", + "stopAtEntry": false, + "justMyCode": false + }, { "name": ".NET Core Launch (InfoPrint)", "type": "coreclr", diff --git a/ExtractionTool/ExtractionTool.csproj b/ExtractionTool/ExtractionTool.csproj new file mode 100644 index 00000000..4325f7ba --- /dev/null +++ b/ExtractionTool/ExtractionTool.csproj @@ -0,0 +1,71 @@ + + + + net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0;net9.0 + Exe + false + false + latest + enable + true + true + 1.8.7 + + + + + win-x86;win-x64 + + + win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64 + + + win-x86;win-x64;win-arm64;linux-x64;linux-arm64;osx-x64;osx-arm64 + + + net6.0;net7.0;net8.0;net9.0 + + + + + $(DefineConstants);WINX86 + + + $(DefineConstants);WINX64 + + + + + + + + + + + + + + \ No newline at end of file diff --git a/ExtractionTool/Options.cs b/ExtractionTool/Options.cs new file mode 100644 index 00000000..18956c25 --- /dev/null +++ b/ExtractionTool/Options.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.IO; + +namespace ExtractionTool +{ + /// + /// Set of options for the test executable + /// + internal sealed class Options + { + #region Properties + + /// + /// Enable debug output for relevant operations + /// + public bool Debug { get; private set; } = false; + + /// + /// Set of input paths to use for operations + /// + public List InputPaths { get; private set; } = []; + + /// + /// Output path for archive extraction + /// + public string OutputPath { get; private set; } = string.Empty; + + #endregion + + /// + /// Parse commandline arguments into an Options object + /// + public static Options? ParseOptions(string[] args) + { + // If we have invalid arguments + if (args == null || args.Length == 0) + return null; + + // Create an Options object + var options = new Options(); + + // Parse the options and paths + for (int index = 0; index < args.Length; index++) + { + string arg = args[index]; + switch (arg) + { + case "-?": + case "-h": + case "--help": + return null; + + case "-d": + case "--debug": + options.Debug = true; + break; + + case "-o": + case "--outdir": + options.OutputPath = index + 1 < args.Length ? args[++index] : string.Empty; + break; + + default: + options.InputPaths.Add(arg); + break; + } + } + + // Validate we have any input paths to work on + if (options.InputPaths.Count == 0) + { + Console.WriteLine("At least one path is required!"); + return null; + } + + // Validate the output path + bool validPath = ValidateExtractionPath(options); + if (!validPath) + return null; + + return options; + } + + /// + /// Display help text + /// + public static void DisplayHelp() + { + Console.WriteLine("Extraction Tool"); + Console.WriteLine(); + Console.WriteLine("ExtractionTool.exe file|directory ..."); + Console.WriteLine(); + Console.WriteLine("Options:"); + Console.WriteLine("-?, -h, --help Display this help text and quit"); + Console.WriteLine("-d, --debug Enable debug mode"); + Console.WriteLine("-o, --outdir [PATH] Set output path for extraction (required)"); + } + + /// + /// Validate the extraction path + /// + private static bool ValidateExtractionPath(Options options) + { + // Null or empty output path + if (string.IsNullOrEmpty(options.OutputPath)) + { + Console.WriteLine("Output directory required for extraction!"); + Console.WriteLine(); + return false; + } + + // Malformed output path or invalid location + try + { + options.OutputPath = Path.GetFullPath(options.OutputPath); + Directory.CreateDirectory(options.OutputPath); + } + catch + { + Console.WriteLine("Output directory could not be created!"); + Console.WriteLine(); + return false; + } + + return true; + } + } +} \ No newline at end of file diff --git a/ExtractionTool/Program.cs b/ExtractionTool/Program.cs new file mode 100644 index 00000000..26e94ee0 --- /dev/null +++ b/ExtractionTool/Program.cs @@ -0,0 +1,424 @@ +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; + } + } + } +} diff --git a/SabreTools.Serialization.sln b/SabreTools.Serialization.sln index 7c7b5ceb..ee56a1c1 100644 --- a/SabreTools.Serialization.sln +++ b/SabreTools.Serialization.sln @@ -9,26 +9,68 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "InfoPrint", "InfoPrint\Info EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SabreTools.Serialization.Test", "SabreTools.Serialization.Test\SabreTools.Serialization.Test.csproj", "{B8A04C5E-A14F-4842-9035-2F6871A1DA10}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExtractionTool", "ExtractionTool\ExtractionTool.csproj", "{1565A8FD-1399-4CA7-A806-11FCD11EC687}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU + Debug|x64 = Debug|x64 + Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE + Release|x64 = Release|x64 + Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {5B688801-5F36-483E-B2E8-F219BA5923A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5B688801-5F36-483E-B2E8-F219BA5923A2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5B688801-5F36-483E-B2E8-F219BA5923A2}.Debug|x64.ActiveCfg = Debug|Any CPU + {5B688801-5F36-483E-B2E8-F219BA5923A2}.Debug|x64.Build.0 = Debug|Any CPU + {5B688801-5F36-483E-B2E8-F219BA5923A2}.Debug|x86.ActiveCfg = Debug|Any CPU + {5B688801-5F36-483E-B2E8-F219BA5923A2}.Debug|x86.Build.0 = Debug|Any CPU {5B688801-5F36-483E-B2E8-F219BA5923A2}.Release|Any CPU.ActiveCfg = Release|Any CPU {5B688801-5F36-483E-B2E8-F219BA5923A2}.Release|Any CPU.Build.0 = Release|Any CPU + {5B688801-5F36-483E-B2E8-F219BA5923A2}.Release|x64.ActiveCfg = Release|Any CPU + {5B688801-5F36-483E-B2E8-F219BA5923A2}.Release|x64.Build.0 = Release|Any CPU + {5B688801-5F36-483E-B2E8-F219BA5923A2}.Release|x86.ActiveCfg = Release|Any CPU + {5B688801-5F36-483E-B2E8-F219BA5923A2}.Release|x86.Build.0 = Release|Any CPU {F3DEE31A-4726-464C-A90C-C19D78F51898}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F3DEE31A-4726-464C-A90C-C19D78F51898}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F3DEE31A-4726-464C-A90C-C19D78F51898}.Debug|x64.ActiveCfg = Debug|Any CPU + {F3DEE31A-4726-464C-A90C-C19D78F51898}.Debug|x64.Build.0 = Debug|Any CPU + {F3DEE31A-4726-464C-A90C-C19D78F51898}.Debug|x86.ActiveCfg = Debug|Any CPU + {F3DEE31A-4726-464C-A90C-C19D78F51898}.Debug|x86.Build.0 = Debug|Any CPU {F3DEE31A-4726-464C-A90C-C19D78F51898}.Release|Any CPU.ActiveCfg = Release|Any CPU {F3DEE31A-4726-464C-A90C-C19D78F51898}.Release|Any CPU.Build.0 = Release|Any CPU + {F3DEE31A-4726-464C-A90C-C19D78F51898}.Release|x64.ActiveCfg = Release|Any CPU + {F3DEE31A-4726-464C-A90C-C19D78F51898}.Release|x64.Build.0 = Release|Any CPU + {F3DEE31A-4726-464C-A90C-C19D78F51898}.Release|x86.ActiveCfg = Release|Any CPU + {F3DEE31A-4726-464C-A90C-C19D78F51898}.Release|x86.Build.0 = Release|Any CPU {B8A04C5E-A14F-4842-9035-2F6871A1DA10}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B8A04C5E-A14F-4842-9035-2F6871A1DA10}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B8A04C5E-A14F-4842-9035-2F6871A1DA10}.Debug|x64.ActiveCfg = Debug|Any CPU + {B8A04C5E-A14F-4842-9035-2F6871A1DA10}.Debug|x64.Build.0 = Debug|Any CPU + {B8A04C5E-A14F-4842-9035-2F6871A1DA10}.Debug|x86.ActiveCfg = Debug|Any CPU + {B8A04C5E-A14F-4842-9035-2F6871A1DA10}.Debug|x86.Build.0 = Debug|Any CPU {B8A04C5E-A14F-4842-9035-2F6871A1DA10}.Release|Any CPU.ActiveCfg = Release|Any CPU {B8A04C5E-A14F-4842-9035-2F6871A1DA10}.Release|Any CPU.Build.0 = Release|Any CPU + {B8A04C5E-A14F-4842-9035-2F6871A1DA10}.Release|x64.ActiveCfg = Release|Any CPU + {B8A04C5E-A14F-4842-9035-2F6871A1DA10}.Release|x64.Build.0 = Release|Any CPU + {B8A04C5E-A14F-4842-9035-2F6871A1DA10}.Release|x86.ActiveCfg = Release|Any CPU + {B8A04C5E-A14F-4842-9035-2F6871A1DA10}.Release|x86.Build.0 = Release|Any CPU + {1565A8FD-1399-4CA7-A806-11FCD11EC687}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1565A8FD-1399-4CA7-A806-11FCD11EC687}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1565A8FD-1399-4CA7-A806-11FCD11EC687}.Debug|x64.ActiveCfg = Debug|Any CPU + {1565A8FD-1399-4CA7-A806-11FCD11EC687}.Debug|x64.Build.0 = Debug|Any CPU + {1565A8FD-1399-4CA7-A806-11FCD11EC687}.Debug|x86.ActiveCfg = Debug|Any CPU + {1565A8FD-1399-4CA7-A806-11FCD11EC687}.Debug|x86.Build.0 = Debug|Any CPU + {1565A8FD-1399-4CA7-A806-11FCD11EC687}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1565A8FD-1399-4CA7-A806-11FCD11EC687}.Release|Any CPU.Build.0 = Release|Any CPU + {1565A8FD-1399-4CA7-A806-11FCD11EC687}.Release|x64.ActiveCfg = Release|Any CPU + {1565A8FD-1399-4CA7-A806-11FCD11EC687}.Release|x64.Build.0 = Release|Any CPU + {1565A8FD-1399-4CA7-A806-11FCD11EC687}.Release|x86.ActiveCfg = Release|Any CPU + {1565A8FD-1399-4CA7-A806-11FCD11EC687}.Release|x86.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE EndGlobalSection EndGlobal diff --git a/SabreTools.Serialization/Wrappers/LZSZDD.cs b/SabreTools.Serialization/Wrappers/LZSZDD.cs index acfd6fc5..fb175af1 100644 --- a/SabreTools.Serialization/Wrappers/LZSZDD.cs +++ b/SabreTools.Serialization/Wrappers/LZSZDD.cs @@ -100,6 +100,10 @@ namespace SabreTools.Serialization.Wrappers /// Original name of the file to convert to the output name public bool Extract(string filename, string outputDirectory, bool includeDebug) { + // Ensure the filename + if (filename.Length == 0 && Filename != null) + filename = Filename; + // Get the length of the compressed data long compressedSize = Length - 14; if (compressedSize < 14) diff --git a/SabreTools.Serialization/Wrappers/XZ.cs b/SabreTools.Serialization/Wrappers/XZ.cs index 483aea83..8dd7a6d5 100644 --- a/SabreTools.Serialization/Wrappers/XZ.cs +++ b/SabreTools.Serialization/Wrappers/XZ.cs @@ -85,7 +85,7 @@ namespace SabreTools.Serialization.Wrappers #region Extraction /// - public bool Extract(string outDir, bool includeDebug) + public bool Extract(string outputDirectory, bool includeDebug) { #if NET462_OR_GREATER || NETCOREAPP if (_dataSource == null || !_dataSource.CanRead) @@ -104,7 +104,7 @@ namespace SabreTools.Serialization.Wrappers filename = filename.Replace('\\', '/'); // Ensure the full output directory exists - filename = Path.Combine(outDir, filename); + filename = Path.Combine(outputDirectory, filename); var directoryName = Path.GetDirectoryName(filename); if (directoryName != null && !Directory.Exists(directoryName)) Directory.CreateDirectory(directoryName); diff --git a/publish-nix.sh b/publish-nix.sh index d93c7a4f..33999bb5 100755 --- a/publish-nix.sh +++ b/publish-nix.sh @@ -72,6 +72,45 @@ if [ $NO_BUILD = false ]; then # Create Nuget Package dotnet pack SabreTools.Serialization/SabreTools.Serialization.csproj --output $BUILD_FOLDER + # Build ExtractionTool + for FRAMEWORK in "${FRAMEWORKS[@]}"; do + for RUNTIME in "${RUNTIMES[@]}"; do + # Output the current build + echo "===== Build ExtractionTool - $FRAMEWORK, $RUNTIME =====" + + # If we have an invalid combination of framework and runtime + if [[ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then + if [[ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]; then + echo "Skipped due to invalid combination" + continue + fi + fi + + # If we have Apple silicon but an unsupported framework + if [[ ! $(echo ${VALID_APPLE_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then + if [ $RUNTIME = "osx-arm64" ]; then + echo "Skipped due to no Apple Silicon support" + continue + fi + fi + + # Only .NET 5 and above can publish to a single file + if [[ $(echo ${SINGLE_FILE_CAPABLE[@]} | fgrep -w $FRAMEWORK) ]]; then + # Only include Debug if set + if [ $INCLUDE_DEBUG = true ]; then + dotnet publish ExtractionTool/ExtractionTool.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true + fi + dotnet publish ExtractionTool/ExtractionTool.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false + else + # Only include Debug if set + if [ $INCLUDE_DEBUG = true ]; then + dotnet publish ExtractionTool/ExtractionTool.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT + fi + dotnet publish ExtractionTool/ExtractionTool.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false + fi + done + done + # Build InfoPrint for FRAMEWORK in "${FRAMEWORKS[@]}"; do for RUNTIME in "${RUNTIMES[@]}"; do @@ -114,6 +153,38 @@ fi # Only create archives if requested if [ $NO_ARCHIVE = false ]; then + # Create ExtractionTool archives + for FRAMEWORK in "${FRAMEWORKS[@]}"; do + for RUNTIME in "${RUNTIMES[@]}"; do + # Output the current build + echo "===== Archive ExtractionTool - $FRAMEWORK, $RUNTIME =====" + + # If we have an invalid combination of framework and runtime + if [[ ! $(echo ${VALID_CROSS_PLATFORM_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then + if [[ $(echo ${VALID_CROSS_PLATFORM_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]; then + echo "Skipped due to invalid combination" + continue + fi + fi + + # If we have Apple silicon but an unsupported framework + if [[ ! $(echo ${VALID_APPLE_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then + if [ $RUNTIME = "osx-arm64" ]; then + echo "Skipped due to no Apple Silicon support" + continue + fi + fi + + # Only include Debug if set + if [ $INCLUDE_DEBUG = true ]; then + cd $BUILD_FOLDER/ExtractionTool/bin/Debug/${FRAMEWORK}/${RUNTIME}/publish/ + zip -r $BUILD_FOLDER/ExtractionTool_${FRAMEWORK}_${RUNTIME}_debug.zip . + fi + cd $BUILD_FOLDER/ExtractionTool/bin/Release/${FRAMEWORK}/${RUNTIME}/publish/ + zip -r $BUILD_FOLDER/ExtractionTool_${FRAMEWORK}_${RUNTIME}_release.zip . + done + done + # Create InfoPrint archives for FRAMEWORK in "${FRAMEWORKS[@]}"; do for RUNTIME in "${RUNTIMES[@]}"; do diff --git a/publish-win.ps1 b/publish-win.ps1 index 4371b6a5..71e1ee3d 100644 --- a/publish-win.ps1 +++ b/publish-win.ps1 @@ -63,6 +63,42 @@ if (!$NO_BUILD.IsPresent) { # Create Nuget Package dotnet pack SabreTools.Serialization\SabreTools.Serialization.csproj --output $BUILD_FOLDER + # Build ExtractionTool + foreach ($FRAMEWORK in $FRAMEWORKS) { + foreach ($RUNTIME in $RUNTIMES) { + # Output the current build + Write-Host "===== Build ExtractionTool - $FRAMEWORK, $RUNTIME =====" + + # If we have an invalid combination of framework and runtime + if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) { + Write-Host "Skipped due to invalid combination" + continue + } + + # If we have Apple silicon but an unsupported framework + if ($VALID_APPLE_FRAMEWORKS -notcontains $FRAMEWORK -and $RUNTIME -eq 'osx-arm64') { + Write-Host "Skipped due to no Apple Silicon support" + continue + } + + # Only .NET 5 and above can publish to a single file + if ($SINGLE_FILE_CAPABLE -contains $FRAMEWORK) { + # Only include Debug if set + if ($INCLUDE_DEBUG.IsPresent) { + dotnet publish ExtractionTool\ExtractionTool.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true + } + dotnet publish ExtractionTool\ExtractionTool.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:PublishSingleFile=true -p:DebugType=None -p:DebugSymbols=false + } + else { + # Only include Debug if set + if ($INCLUDE_DEBUG.IsPresent) { + dotnet publish ExtractionTool\ExtractionTool.csproj -f $FRAMEWORK -r $RUNTIME -c Debug --self-contained true --version-suffix $COMMIT + } + dotnet publish ExtractionTool\ExtractionTool.csproj -f $FRAMEWORK -r $RUNTIME -c Release --self-contained true --version-suffix $COMMIT -p:DebugType=None -p:DebugSymbols=false + } + } + } + # Build InfoPrint foreach ($FRAMEWORK in $FRAMEWORKS) { foreach ($RUNTIME in $RUNTIMES) { @@ -102,11 +138,40 @@ if (!$NO_BUILD.IsPresent) { # Only create archives if requested if (!$NO_ARCHIVE.IsPresent) { + # Create ExtractionTool archives + foreach ($FRAMEWORK in $FRAMEWORKS) { + foreach ($RUNTIME in $RUNTIMES) { + # Output the current build + Write-Host "===== Archive ExtractionTool - $FRAMEWORK, $RUNTIME =====" + + # If we have an invalid combination of framework and runtime + if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) { + Write-Host "Skipped due to invalid combination" + continue + } + + # If we have Apple silicon but an unsupported framework + if ($VALID_APPLE_FRAMEWORKS -notcontains $FRAMEWORK -and $RUNTIME -eq 'osx-arm64') { + Write-Host "Skipped due to no Apple Silicon support" + continue + } + + # Only include Debug if set + if ($INCLUDE_DEBUG.IsPresent) { + Set-Location -Path $BUILD_FOLDER\ExtractionTool\bin\Debug\${FRAMEWORK}\${RUNTIME}\publish\ + 7z a -tzip $BUILD_FOLDER\ExtractionTool_${FRAMEWORK}_${RUNTIME}_debug.zip * + } + + Set-Location -Path $BUILD_FOLDER\ExtractionTool\bin\Release\${FRAMEWORK}\${RUNTIME}\publish\ + 7z a -tzip $BUILD_FOLDER\ExtractionTool_${FRAMEWORK}_${RUNTIME}_release.zip * + } + } + # Create InfoPrint archives foreach ($FRAMEWORK in $FRAMEWORKS) { foreach ($RUNTIME in $RUNTIMES) { # Output the current build - Write-Host "===== Archive Program - $FRAMEWORK, $RUNTIME =====" + Write-Host "===== Archive InfoPrint - $FRAMEWORK, $RUNTIME =====" # If we have an invalid combination of framework and runtime if ($VALID_CROSS_PLATFORM_FRAMEWORKS -notcontains $FRAMEWORK -and $VALID_CROSS_PLATFORM_RUNTIMES -contains $RUNTIME) {