diff --git a/.github/workflows/build_test.yml b/.github/workflows/build_test.yml
index 044d1db8..69a187c6 100644
--- a/.github/workflows/build_test.yml
+++ b/.github/workflows/build_test.yml
@@ -10,7 +10,7 @@ jobs:
strategy:
matrix:
- project: [ProtectionScan, Test]
+ project: [ExtractionTool, ProtectionScan, Test]
runtime: [win-x86, win-x64, win-arm64, linux-x64, linux-arm64, osx-x64]
framework: [net8.0] #[net20, net35, net40, net452, net472, net48, netcoreapp3.1, net5.0, net6.0, net7.0, net8.0]
conf: [Release, Debug]
diff --git a/BinaryObjectScanner.sln b/BinaryObjectScanner.sln
index 69a17a5d..256be9e4 100644
--- a/BinaryObjectScanner.sln
+++ b/BinaryObjectScanner.sln
@@ -20,6 +20,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "BinaryObjectScanner", "Bina
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ProtectionScan", "ProtectionScan\ProtectionScan.csproj", "{14CC56E0-7D56-497C-BF3D-4C06FA169831}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ExtractionTool", "ExtractionTool\ExtractionTool.csproj", "{89767A19-043F-4251-805B-B2CBC48E2B79}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -38,6 +40,10 @@ Global
{14CC56E0-7D56-497C-BF3D-4C06FA169831}.Debug|Any CPU.Build.0 = Debug|Any CPU
{14CC56E0-7D56-497C-BF3D-4C06FA169831}.Release|Any CPU.ActiveCfg = Release|Any CPU
{14CC56E0-7D56-497C-BF3D-4C06FA169831}.Release|Any CPU.Build.0 = Release|Any CPU
+ {89767A19-043F-4251-805B-B2CBC48E2B79}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {89767A19-043F-4251-805B-B2CBC48E2B79}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {89767A19-043F-4251-805B-B2CBC48E2B79}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {89767A19-043F-4251-805B-B2CBC48E2B79}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
diff --git a/ExtractionTool/ExtractionTool.csproj b/ExtractionTool/ExtractionTool.csproj
new file mode 100644
index 00000000..5a1dfdd5
--- /dev/null
+++ b/ExtractionTool/ExtractionTool.csproj
@@ -0,0 +1,42 @@
+
+
+
+ net20;net35;net40;net452;net462;net472;net48;netcoreapp3.1;net5.0;net6.0;net7.0;net8.0
+ Exe
+ false
+ false
+ latest
+ enable
+ true
+ true
+
+
+
+
+ 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
+
+
+
+
+ $(DefineConstants);WIN
+
+
+
+
+
+
+
+
+
+
+
+
\ 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..f6a38820
--- /dev/null
+++ b/ExtractionTool/Program.cs
@@ -0,0 +1,441 @@
+using System;
+using System.IO;
+using BinaryObjectScanner.FileType;
+using SabreTools.IO.Extensions;
+using WrapperFactory = SabreTools.Serialization.Wrappers.WrapperFactory;
+using WrapperType = SabreTools.Serialization.Wrappers.WrapperType;
+
+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)
+ {
+ 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
+ {
+ stream.Read(magic, 0, 16);
+ stream.Seek(0, SeekOrigin.Begin);
+ }
+ catch (Exception ex)
+ {
+ if (includeDebug) Console.WriteLine(ex);
+ return;
+ }
+
+ // Get the file type
+ WrapperType ft = WrapperFactory.GetFileType(magic, extension);
+
+ // Executables technically can be "extracted", but let's ignore that
+ // TODO: Support executables that include other stuff
+
+ // 7-zip
+ if (ft == WrapperType.SevenZip)
+ {
+ // Build the archive information
+ 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
+ // Extract using the FileType
+ var sevenZip = new SevenZip();
+ sevenZip.Extract(stream, file, outputDirectory, includeDebug: true);
+#endif
+ }
+
+ // BFPK archive
+ else if (ft == WrapperType.BFPK)
+ {
+ // Build the BFPK information
+ Console.WriteLine("Extracting BFPK contents");
+ Console.WriteLine();
+
+ // Extract using the FileType
+ var bfpk = new BFPK();
+ bfpk.Extract(stream, file, outputDirectory, includeDebug: true);
+ }
+
+ // BSP
+ else if (ft == WrapperType.BSP)
+ {
+ // Build the BSP information
+ Console.WriteLine("Extracting BSP contents");
+ Console.WriteLine();
+
+ // Extract using the FileType
+ var bsp = new BSP();
+ bsp.Extract(stream, file, outputDirectory, includeDebug: true);
+ }
+
+ // bzip2
+ else if (ft == WrapperType.BZip2)
+ {
+ // Build the bzip2 information
+ Console.WriteLine("Extracting bzip2 contents");
+ Console.WriteLine();
+
+#if NET20 || NET35 || NET40 || NET452
+ Console.WriteLine("Extraction is not supported for this framework!");
+ Console.WriteLine();
+#else
+ // Extract using the FileType
+ var bzip2 = new BZip2();
+ bzip2.Extract(stream, file, outputDirectory, includeDebug: true);
+#endif
+ }
+
+ // CFB
+ else if (ft == WrapperType.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
+ // Extract using the FileType
+ var cfb = new CFB();
+ cfb.Extract(stream, file, outputDirectory, includeDebug: true);
+#endif
+ }
+
+ // GCF
+ else if (ft == WrapperType.GCF)
+ {
+ // Build the GCF information
+ Console.WriteLine("Extracting GCF contents");
+ Console.WriteLine();
+
+ // Extract using the FileType
+ var gcf = new GCF();
+ gcf.Extract(stream, file, outputDirectory, includeDebug: true);
+ }
+
+ // gzip
+ else if (ft == WrapperType.GZIP)
+ {
+ // Build the gzip information
+ Console.WriteLine("Extracting gzip contents");
+ Console.WriteLine();
+
+#if NET20 || NET35 || NET40 || NET452
+ Console.WriteLine("Extraction is not supported for this framework!");
+ Console.WriteLine();
+#else
+ // Extract using the FileType
+ var gzip = new GZIP();
+ gzip.Extract(stream, file, outputDirectory, includeDebug: true);
+#endif
+ }
+
+ // InstallShield Archive V3 (Z)
+ else if (ft == WrapperType.InstallShieldArchiveV3)
+ {
+ // Build the InstallShield Archive V3 information
+ Console.WriteLine("Extracting InstallShield Archive V3 contents");
+ Console.WriteLine();
+
+ // Extract using the FileType
+ var isav3 = new InstallShieldArchiveV3();
+ isav3.Extract(stream, file, outputDirectory, includeDebug: true);
+ }
+
+ // IS-CAB archive
+ else if (ft == WrapperType.InstallShieldCAB)
+ {
+ // Build the archive information
+ Console.WriteLine("Extracting IS-CAB contents");
+ Console.WriteLine();
+
+ // Extract using the FileType
+ var iscab = new InstallShieldCAB();
+ iscab.Extract(stream, file, outputDirectory, includeDebug: true);
+ }
+
+ // Microsoft Cabinet archive
+ else if (ft == WrapperType.MicrosoftCAB)
+ {
+ // Build the cabinet information
+ Console.WriteLine("Extracting MS-CAB contents");
+ Console.WriteLine();
+
+#if NET20 || NET35 || !WIN
+ Console.WriteLine("Extraction is not supported for this framework!");
+ Console.WriteLine();
+#else
+ // Extract using the FileType
+ var mscab = new MicrosoftCAB();
+ mscab.Extract(stream, file, outputDirectory, includeDebug: true);
+#endif
+ }
+
+ // Microsoft LZ / LZ32
+ else if (ft == WrapperType.MicrosoftLZ)
+ {
+ // Build the Microsoft LZ / LZ32 information
+ Console.WriteLine("Extracting Microsoft LZ / LZ32 contents");
+ Console.WriteLine();
+
+ // Extract using the FileType
+ var lz = new MicrosoftLZ();
+ lz.Extract(stream, file, outputDirectory, includeDebug: true);
+ }
+
+ // MoPaQ (MPQ) archive
+ else if (ft == WrapperType.MoPaQ)
+ {
+ // Build the cabinet information
+ Console.WriteLine("Extracting MoPaQ contents");
+ Console.WriteLine();
+
+#if NET20 || NET35 || NET40 || !WIN
+ Console.WriteLine("Extraction is not supported for this framework!");
+ Console.WriteLine();
+#else
+ // Extract using the FileType
+ var mpq = new MPQ();
+ mpq.Extract(stream, file, outputDirectory, includeDebug: true);
+#endif
+ }
+
+ // PAK
+ else if (ft == WrapperType.PAK)
+ {
+ // Build the archive information
+ Console.WriteLine("Extracting PAK contents");
+ Console.WriteLine();
+
+ // Extract using the FileType
+ var pak = new PAK();
+ pak.Extract(stream, file, outputDirectory, includeDebug: true);
+ }
+
+ // PFF
+ else if (ft == WrapperType.PFF)
+ {
+ // Build the archive information
+ Console.WriteLine("Extracting PFF contents");
+ Console.WriteLine();
+
+ // Extract using the FileType
+ var pff = new PFF();
+ pff.Extract(stream, file, outputDirectory, includeDebug: true);
+ }
+
+ // PKZIP
+ else if (ft == WrapperType.PKZIP)
+ {
+ // Build the archive information
+ Console.WriteLine("Extracting PKZIP contents");
+ Console.WriteLine();
+
+#if NET20 || NET35 || NET40 || NET452
+ Console.WriteLine("Extraction is not supported for this framework!");
+ Console.WriteLine();
+#else
+ // Extract using the FileType
+ var pkzip = new PKZIP();
+ pkzip.Extract(stream, file, outputDirectory, includeDebug: true);
+#endif
+ }
+
+ // Quantum
+ else if (ft == WrapperType.Quantum)
+ {
+ // Build the archive information
+ Console.WriteLine("Extracting Quantum contents");
+ Console.WriteLine();
+
+ // Extract using the FileType
+ var quantum = new Quantum();
+ quantum.Extract(stream, file, outputDirectory, includeDebug: true);
+ }
+
+ // RAR
+ else if (ft == WrapperType.RAR)
+ {
+ // Build the archive information
+ Console.WriteLine("Extracting RAR contents");
+ Console.WriteLine();
+
+#if NET20 || NET35 || NET40 || NET452
+ Console.WriteLine("Extraction is not supported for this framework!");
+ Console.WriteLine();
+#else
+ // Extract using the FileType
+ var rar = new RAR();
+ rar.Extract(stream, file, outputDirectory, includeDebug: true);
+#endif
+ }
+
+ // SGA
+ else if (ft == WrapperType.SGA)
+ {
+ // Build the archive information
+ Console.WriteLine("Extracting SGA contents");
+ Console.WriteLine();
+
+ // Extract using the FileType
+ var sga = new SGA();
+ sga.Extract(stream, file, outputDirectory, includeDebug: true);
+ }
+
+ // Tape Archive
+ else if (ft == WrapperType.TapeArchive)
+ {
+ // Build the archive information
+ 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
+ // Extract using the FileType
+ var tar = new TapeArchive();
+ tar.Extract(stream, file, outputDirectory, includeDebug: true);
+#endif
+ }
+
+ // VBSP
+ else if (ft == WrapperType.VBSP)
+ {
+ // Build the archive information
+ Console.WriteLine("Extracting VBSP contents");
+ Console.WriteLine();
+
+ // Extract using the FileType
+ var vbsp = new VBSP();
+ vbsp.Extract(stream, file, outputDirectory, includeDebug: true);
+ }
+
+ // VPK
+ else if (ft == WrapperType.VPK)
+ {
+ // Build the archive information
+ Console.WriteLine("Extracting VPK contents");
+ Console.WriteLine();
+
+ // Extract using the FileType
+ var vpk = new VPK();
+ vpk.Extract(stream, file, outputDirectory, includeDebug: true);
+ }
+
+ // WAD
+ else if (ft == WrapperType.WAD)
+ {
+ // Build the archive information
+ Console.WriteLine("Extracting WAD contents");
+ Console.WriteLine();
+
+ // Extract using the FileType
+ var wad = new WAD();
+ wad.Extract(stream, file, outputDirectory, includeDebug: true);
+ }
+
+ // xz
+ else if (ft == WrapperType.XZ)
+ {
+ // Build the xz information
+ Console.WriteLine("Extracting xz contents");
+ Console.WriteLine();
+
+#if NET20 || NET35 || NET40 || NET452
+ Console.WriteLine("Extraction is not supported for this framework!");
+ Console.WriteLine();
+#else
+ // Extract using the FileType
+ var xz = new XZ();
+ xz.Extract(stream, file, outputDirectory, includeDebug: true);
+#endif
+ }
+
+ // XZP
+ else if (ft == WrapperType.XZP)
+ {
+ // Build the archive information
+ Console.WriteLine("Extracting XZP contents");
+ Console.WriteLine();
+
+ // Extract using the FileType
+ var xzp = new XZP();
+ xzp.Extract(stream, file, outputDirectory, includeDebug: true);
+ }
+
+ // Everything else
+ else
+ {
+ Console.WriteLine("Not a supported extractable file format, skipping...");
+ Console.WriteLine();
+ return;
+ }
+ }
+ }
+}
diff --git a/publish-nix.sh b/publish-nix.sh
index 0c61ce07..804b6199 100644
--- a/publish-nix.sh
+++ b/publish-nix.sh
@@ -69,6 +69,45 @@ if [ $NO_BUILD = false ]; then
# Create Nuget Package
dotnet pack BinaryObjectScanner/BinaryObjectScanner.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 building all
+ if [ $USE_ALL = 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 building all
+ if [ $USE_ALL = 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 ProtectionScan
for FRAMEWORK in "${FRAMEWORKS[@]}"; do
for RUNTIME in "${RUNTIMES[@]}"; do
@@ -150,6 +189,50 @@ 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 building all
+ if [ $USE_ALL = true ]; then
+ cd $BUILD_FOLDER/ExtractionTool/bin/Debug/${FRAMEWORK}/${RUNTIME}/publish/
+ if [[ $(echo ${NON_DLL_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
+ zip -r $BUILD_FOLDER/ExtractionTool_${FRAMEWORK}_${RUNTIME}_debug.zip . -x 'CascLib.dll' -x 'mspack.dll' -x 'StormLib.dll'
+ elif [[ $(echo ${NON_DLL_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]; then
+ zip -r $BUILD_FOLDER/ExtractionTool_${FRAMEWORK}_${RUNTIME}_debug.zip . -x 'CascLib.dll' -x 'mspack.dll' -x 'StormLib.dll'
+ else
+ zip -r $BUILD_FOLDER/ExtractionTool_${FRAMEWORK}_${RUNTIME}_debug.zip .
+ fi
+ fi
+ cd $BUILD_FOLDER/ExtractionTool/bin/Release/${FRAMEWORK}/${RUNTIME}/publish/
+ if [[ $(echo ${NON_DLL_FRAMEWORKS[@]} | fgrep -w $FRAMEWORK) ]]; then
+ zip -r $BUILD_FOLDER/ExtractionTool_${FRAMEWORK}_${RUNTIME}_release.zip . -x 'CascLib.dll' -x 'mspack.dll' -x 'StormLib.dll'
+ elif [[ $(echo ${NON_DLL_RUNTIMES[@]} | fgrep -w $RUNTIME) ]]; then
+ zip -r $BUILD_FOLDER/ExtractionTool_${FRAMEWORK}_${RUNTIME}_release.zip . -x 'CascLib.dll' -x 'mspack.dll' -x 'StormLib.dll'
+ else
+ zip -r $BUILD_FOLDER/ExtractionTool_${FRAMEWORK}_${RUNTIME}_release.zip .
+ fi
+ done
+ done
+
# Create ProtectionScan archives
for FRAMEWORK in "${FRAMEWORKS[@]}"; do
for RUNTIME in "${RUNTIMES[@]}"; do
diff --git a/publish-win.ps1 b/publish-win.ps1
index a14d7f56..1424b840 100644
--- a/publish-win.ps1
+++ b/publish-win.ps1
@@ -60,6 +60,42 @@ if (!$NO_BUILD.IsPresent) {
# Create Nuget Package
dotnet pack BinaryObjectScanner\BinaryObjectScanner.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 building all
+ if ($USE_ALL.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 building all
+ if ($USE_ALL.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 ProtectionScan
foreach ($FRAMEWORK in $FRAMEWORKS) {
foreach ($RUNTIME in $RUNTIMES) {
@@ -135,6 +171,45 @@ 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 building all
+ if ($USE_ALL.IsPresent) {
+ Set-Location -Path $BUILD_FOLDER\ExtractionTool\bin\Debug\${FRAMEWORK}\${RUNTIME}\publish\
+ if ($NON_DLL_FRAMEWORKS -contains $FRAMEWORK -or $NON_DLL_RUNTIMES -contains $RUNTIME) {
+ 7z a -tzip -x'!CascLib.dll' -x'!mspack.dll' -x'!StormLib.dll' $BUILD_FOLDER\ExtractionTool_${FRAMEWORK}_${RUNTIME}_debug.zip *
+ }
+ else {
+ 7z a -tzip $BUILD_FOLDER\ExtractionTool_${FRAMEWORK}_${RUNTIME}_debug.zip *
+ }
+ }
+
+ Set-Location -Path $BUILD_FOLDER\ExtractionTool\bin\Release\${FRAMEWORK}\${RUNTIME}\publish\
+ if ($NON_DLL_FRAMEWORKS -contains $FRAMEWORK -or $NON_DLL_RUNTIMES -contains $RUNTIME) {
+ 7z a -tzip -x'!CascLib.dll' -x'!mspack.dll' -x'!StormLib.dll' $BUILD_FOLDER\ExtractionTool_${FRAMEWORK}_${RUNTIME}_release.zip *
+ }
+ else {
+ 7z a -tzip $BUILD_FOLDER\ExtractionTool_${FRAMEWORK}_${RUNTIME}_release.zip *
+ }
+ }
+ }
+
# Create ProtectionScan archives
foreach ($FRAMEWORK in $FRAMEWORKS) {
foreach ($RUNTIME in $RUNTIMES) {