diff --git a/BinaryObjectScanner.Test/BinaryObjectScanner.Test.csproj b/BinaryObjectScanner.Test/BinaryObjectScanner.Test.csproj index c8281aa3..0784c689 100644 --- a/BinaryObjectScanner.Test/BinaryObjectScanner.Test.csproj +++ b/BinaryObjectScanner.Test/BinaryObjectScanner.Test.csproj @@ -17,7 +17,7 @@ all - + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/BinaryObjectScanner.Test/FactoryTests.cs b/BinaryObjectScanner.Test/FactoryTests.cs index ae20bd2c..3771c927 100644 --- a/BinaryObjectScanner.Test/FactoryTests.cs +++ b/BinaryObjectScanner.Test/FactoryTests.cs @@ -68,8 +68,10 @@ namespace BinaryObjectScanner.Test WrapperType.GZIP, WrapperType.InstallShieldArchiveV3, WrapperType.InstallShieldCAB, + WrapperType.LZKWAJ, + WrapperType.LZQBasic, + WrapperType.LZSZDD, WrapperType.MicrosoftCAB, - WrapperType.MicrosoftLZ, WrapperType.MoPaQ, //WrapperType.N3DS, //WrapperType.NCF, diff --git a/BinaryObjectScanner.Test/FileType/MicrosoftLZTests.cs b/BinaryObjectScanner.Test/FileType/LZKWAJTests.cs similarity index 85% rename from BinaryObjectScanner.Test/FileType/MicrosoftLZTests.cs rename to BinaryObjectScanner.Test/FileType/LZKWAJTests.cs index 4379246e..2af4a437 100644 --- a/BinaryObjectScanner.Test/FileType/MicrosoftLZTests.cs +++ b/BinaryObjectScanner.Test/FileType/LZKWAJTests.cs @@ -4,14 +4,14 @@ using Xunit; namespace BinaryObjectScanner.Test.FileType { - public class MicrosoftLZTests + public class LZKWAJTests { [Fact] public void ExtractFile_EmptyString_False() { string file = string.Empty; string outDir = string.Empty; - var extractable = new MicrosoftLZ(); + var extractable = new LZKWAJ(); bool actual = extractable.Extract(file, outDir, includeDebug: false); Assert.False(actual); @@ -23,7 +23,7 @@ namespace BinaryObjectScanner.Test.FileType Stream? stream = null; string file = string.Empty; string outDir = string.Empty; - var extractable = new MicrosoftLZ(); + var extractable = new LZKWAJ(); bool actual = extractable.Extract(stream, file, outDir, includeDebug: false); Assert.False(actual); @@ -35,7 +35,7 @@ namespace BinaryObjectScanner.Test.FileType Stream? stream = new MemoryStream(); string file = string.Empty; string outDir = string.Empty; - var extractable = new MicrosoftLZ(); + var extractable = new LZKWAJ(); bool actual = extractable.Extract(stream, file, outDir, includeDebug: false); Assert.False(actual); diff --git a/BinaryObjectScanner.Test/FileType/LZQBasicTests.cs b/BinaryObjectScanner.Test/FileType/LZQBasicTests.cs new file mode 100644 index 00000000..526a7cb9 --- /dev/null +++ b/BinaryObjectScanner.Test/FileType/LZQBasicTests.cs @@ -0,0 +1,44 @@ +using System.IO; +using BinaryObjectScanner.FileType; +using Xunit; + +namespace BinaryObjectScanner.Test.FileType +{ + public class LZQBasicTests + { + [Fact] + public void ExtractFile_EmptyString_False() + { + string file = string.Empty; + string outDir = string.Empty; + var extractable = new LZQBasic(); + + bool actual = extractable.Extract(file, outDir, includeDebug: false); + Assert.False(actual); + } + + [Fact] + public void ExtractStream_Null_False() + { + Stream? stream = null; + string file = string.Empty; + string outDir = string.Empty; + var extractable = new LZQBasic(); + + bool actual = extractable.Extract(stream, file, outDir, includeDebug: false); + Assert.False(actual); + } + + [Fact] + public void ExtractStream_Empty_False() + { + Stream? stream = new MemoryStream(); + string file = string.Empty; + string outDir = string.Empty; + var extractable = new LZQBasic(); + + bool actual = extractable.Extract(stream, file, outDir, includeDebug: false); + Assert.False(actual); + } + } +} diff --git a/BinaryObjectScanner.Test/FileType/LZSZDDTests.cs b/BinaryObjectScanner.Test/FileType/LZSZDDTests.cs new file mode 100644 index 00000000..bbe689ec --- /dev/null +++ b/BinaryObjectScanner.Test/FileType/LZSZDDTests.cs @@ -0,0 +1,44 @@ +using System.IO; +using BinaryObjectScanner.FileType; +using Xunit; + +namespace BinaryObjectScanner.Test.FileType +{ + public class LZSZDDTests + { + [Fact] + public void ExtractFile_EmptyString_False() + { + string file = string.Empty; + string outDir = string.Empty; + var extractable = new LZSZDD(); + + bool actual = extractable.Extract(file, outDir, includeDebug: false); + Assert.False(actual); + } + + [Fact] + public void ExtractStream_Null_False() + { + Stream? stream = null; + string file = string.Empty; + string outDir = string.Empty; + var extractable = new LZSZDD(); + + bool actual = extractable.Extract(stream, file, outDir, includeDebug: false); + Assert.False(actual); + } + + [Fact] + public void ExtractStream_Empty_False() + { + Stream? stream = new MemoryStream(); + string file = string.Empty; + string outDir = string.Empty; + var extractable = new LZSZDD(); + + bool actual = extractable.Extract(stream, file, outDir, includeDebug: false); + Assert.False(actual); + } + } +} diff --git a/BinaryObjectScanner.Test/FileType/SGATests.cs b/BinaryObjectScanner.Test/FileType/SGATests.cs index 8653cb3b..6039c562 100644 --- a/BinaryObjectScanner.Test/FileType/SGATests.cs +++ b/BinaryObjectScanner.Test/FileType/SGATests.cs @@ -40,29 +40,5 @@ namespace BinaryObjectScanner.Test.FileType bool actual = extractable.Extract(stream, file, outDir, includeDebug: false); Assert.False(actual); } - - [Fact] - public void ExtractAll_EmptyModel_False() - { - var model = new SabreTools.Models.SGA.Archive(); - var data = new MemoryStream(); - var item = new SabreTools.Serialization.Wrappers.SGA(model, data); - string outputDirectory = string.Empty; - - bool actual = SGA.ExtractAll(item, outputDirectory); - Assert.False(actual); - } - - [Fact] - public void ExtractFile_EmptyModel_False() - { - var model = new SabreTools.Models.SGA.Archive(); - var data = new MemoryStream(); - var item = new SabreTools.Serialization.Wrappers.SGA(model, data); - string outputDirectory = string.Empty; - - bool actual = SGA.ExtractFile(item, 0, outputDirectory); - Assert.False(actual); - } } } diff --git a/BinaryObjectScanner/BinaryObjectScanner.csproj b/BinaryObjectScanner/BinaryObjectScanner.csproj index 6062eec1..105f988b 100644 --- a/BinaryObjectScanner/BinaryObjectScanner.csproj +++ b/BinaryObjectScanner/BinaryObjectScanner.csproj @@ -87,14 +87,14 @@ - + - - - - + + + + \ No newline at end of file diff --git a/BinaryObjectScanner/Factory.cs b/BinaryObjectScanner/Factory.cs index a42372f9..deb996c3 100644 --- a/BinaryObjectScanner/Factory.cs +++ b/BinaryObjectScanner/Factory.cs @@ -44,8 +44,10 @@ namespace BinaryObjectScanner WrapperType.GZIP => new FileType.GZIP(), WrapperType.InstallShieldArchiveV3 => new FileType.InstallShieldArchiveV3(), WrapperType.InstallShieldCAB => new FileType.InstallShieldCAB(), + WrapperType.LZKWAJ => new FileType.LZKWAJ(), + WrapperType.LZQBasic => new FileType.LZQBasic(), + WrapperType.LZSZDD => new FileType.LZSZDD(), WrapperType.MicrosoftCAB => new FileType.MicrosoftCAB(), - WrapperType.MicrosoftLZ => new FileType.MicrosoftLZ(), WrapperType.MoPaQ => new FileType.MPQ(), //WrapperType.N3DS => new FileType.N3DS(), //WrapperType.NCF => new FileType.NCF(), diff --git a/BinaryObjectScanner/FileType/InstallShieldArchiveV3.cs b/BinaryObjectScanner/FileType/InstallShieldArchiveV3.cs index fcb1c4da..4ac3cd5c 100644 --- a/BinaryObjectScanner/FileType/InstallShieldArchiveV3.cs +++ b/BinaryObjectScanner/FileType/InstallShieldArchiveV3.cs @@ -1,7 +1,5 @@ -using System; -using System.IO; +using System.IO; using BinaryObjectScanner.Interfaces; -using ISv3 = UnshieldSharp.Archive.InstallShieldArchiveV3; namespace BinaryObjectScanner.FileType { @@ -23,41 +21,16 @@ namespace BinaryObjectScanner.FileType /// public bool Extract(Stream? stream, string file, string outDir, bool includeDebug) { - try - { - if (!File.Exists(file)) - return false; - - var archive = new ISv3(file); - foreach (var cfile in archive.Files) - { - try - { - string tempFile = Path.Combine(outDir, cfile.Key); - var directoryName = Path.GetDirectoryName(tempFile); - if (directoryName != null && !Directory.Exists(directoryName)) - Directory.CreateDirectory(directoryName); - - byte[]? fileContents = archive.Extract(cfile.Key, out string? error); - if (fileContents == null || !string.IsNullOrEmpty(error)) - continue; - - using FileStream fs = File.OpenWrite(tempFile); - fs.Write(fileContents, 0, fileContents.Length); - } - catch (Exception ex) - { - if (includeDebug) Console.WriteLine(ex); - } - } - - return true; - } - catch (Exception ex) - { - if (includeDebug) Console.WriteLine(ex); + // Create the wrapper + var isv3 = SabreTools.Serialization.Wrappers.InstallShieldArchiveV3.Create(stream); + if (isv3 == null) return false; - } + + // Loop through and extract all files + Directory.CreateDirectory(outDir); + isv3.ExtractAll(outDir); + + return true; } } } diff --git a/BinaryObjectScanner/FileType/InstallShieldCAB.cs b/BinaryObjectScanner/FileType/InstallShieldCAB.cs index 20750914..d7f9a737 100644 --- a/BinaryObjectScanner/FileType/InstallShieldCAB.cs +++ b/BinaryObjectScanner/FileType/InstallShieldCAB.cs @@ -2,7 +2,7 @@ using System.IO; using System.Text.RegularExpressions; using BinaryObjectScanner.Interfaces; -using UnshieldSharp.Cabinet; +using UnshieldSharp; namespace BinaryObjectScanner.FileType { diff --git a/BinaryObjectScanner/FileType/LZKWAJ.cs b/BinaryObjectScanner/FileType/LZKWAJ.cs new file mode 100644 index 00000000..76ed91c0 --- /dev/null +++ b/BinaryObjectScanner/FileType/LZKWAJ.cs @@ -0,0 +1,36 @@ +using System.IO; +using BinaryObjectScanner.Interfaces; + +namespace BinaryObjectScanner.FileType +{ + /// + /// LZ-compressed file, KWAJ variant + /// + public class LZKWAJ : IExtractable + { + /// + public bool Extract(string file, string outDir, bool includeDebug) + { + if (!File.Exists(file)) + return false; + + using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + return Extract(fs, file, outDir, includeDebug); + } + + /// + public bool Extract(Stream? stream, string file, string outDir, bool includeDebug) + { + // Create the wrapper + var kwaj = SabreTools.Serialization.Wrappers.LZKWAJ.Create(stream); + if (kwaj == null) + return false; + + // Loop through and extract all files + Directory.CreateDirectory(outDir); + kwaj.Extract(outDir); + + return true; + } + } +} diff --git a/BinaryObjectScanner/FileType/LZQBasic.cs b/BinaryObjectScanner/FileType/LZQBasic.cs new file mode 100644 index 00000000..bace4f26 --- /dev/null +++ b/BinaryObjectScanner/FileType/LZQBasic.cs @@ -0,0 +1,36 @@ +using System.IO; +using BinaryObjectScanner.Interfaces; + +namespace BinaryObjectScanner.FileType +{ + /// + /// LZ-compressed file, QBasic variant + /// + public class LZQBasic : IExtractable + { + /// + public bool Extract(string file, string outDir, bool includeDebug) + { + if (!File.Exists(file)) + return false; + + using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + return Extract(fs, file, outDir, includeDebug); + } + + /// + public bool Extract(Stream? stream, string file, string outDir, bool includeDebug) + { + // Create the wrapper + var qbasic = SabreTools.Serialization.Wrappers.LZQBasic.Create(stream); + if (qbasic == null) + return false; + + // Loop through and extract all files + Directory.CreateDirectory(outDir); + qbasic.Extract(outDir); + + return true; + } + } +} diff --git a/BinaryObjectScanner/FileType/LZSZDD.cs b/BinaryObjectScanner/FileType/LZSZDD.cs new file mode 100644 index 00000000..216de535 --- /dev/null +++ b/BinaryObjectScanner/FileType/LZSZDD.cs @@ -0,0 +1,36 @@ +using System.IO; +using BinaryObjectScanner.Interfaces; + +namespace BinaryObjectScanner.FileType +{ + /// + /// LZ-compressed file, SZDD variant + /// + public class LZSZDD : IExtractable + { + /// + public bool Extract(string file, string outDir, bool includeDebug) + { + if (!File.Exists(file)) + return false; + + using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + return Extract(fs, file, outDir, includeDebug); + } + + /// + public bool Extract(Stream? stream, string file, string outDir, bool includeDebug) + { + // Create the wrapper + var szdd = SabreTools.Serialization.Wrappers.LZSZDD.Create(stream); + if (szdd == null) + return false; + + // Loop through and extract all files + Directory.CreateDirectory(outDir); + szdd.Extract(Path.GetFileName(file), outDir); + + return true; + } + } +} diff --git a/BinaryObjectScanner/FileType/MicrosoftLZ.cs b/BinaryObjectScanner/FileType/MicrosoftLZ.cs deleted file mode 100644 index 31b00603..00000000 --- a/BinaryObjectScanner/FileType/MicrosoftLZ.cs +++ /dev/null @@ -1,66 +0,0 @@ -using System; -using System.IO; -using BinaryObjectScanner.Interfaces; -using SabreTools.Compression.LZ; - -namespace BinaryObjectScanner.FileType -{ - /// - /// Microsoft LZ-compressed Files (LZ32) - /// - /// This is treated like an archive type due to the packing style - public class MicrosoftLZ : IExtractable - { - /// - public bool Extract(string file, string outDir, bool includeDebug) - { - if (!File.Exists(file)) - return false; - - using var fs = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); - return Extract(fs, file, outDir, includeDebug); - } - - /// - public bool Extract(Stream? stream, string file, string outDir, bool includeDebug) - { - if (stream == null || !stream.CanRead) - return false; - - try - { - var data = Decompressor.Decompress(stream); - if (data == null) - return false; - - // Create the temp filename - string tempFile = "temp.bin"; - if (!string.IsNullOrEmpty(file)) - { - var expandedFilePath = Decompressor.GetExpandedName(file, out _); - if (expandedFilePath != null) - tempFile = Path.GetFileName(expandedFilePath).TrimEnd('\0'); - if (tempFile.EndsWith(".ex")) - tempFile += "e"; - else if (tempFile.EndsWith(".dl")) - tempFile += "l"; - } - - tempFile = Path.Combine(outDir, tempFile); - var directoryName = Path.GetDirectoryName(tempFile); - if (directoryName != null && !Directory.Exists(directoryName)) - Directory.CreateDirectory(directoryName); - - using Stream tempStream = File.Open(tempFile, FileMode.Create, FileAccess.Write, FileShare.ReadWrite); - tempStream.Write(data, 0, data.Length); - - return true; - } - catch (Exception ex) - { - if (includeDebug) Console.WriteLine(ex); - return false; - } - } - } -} diff --git a/BinaryObjectScanner/FileType/SGA.cs b/BinaryObjectScanner/FileType/SGA.cs index f8790bf3..bba112ce 100644 --- a/BinaryObjectScanner/FileType/SGA.cs +++ b/BinaryObjectScanner/FileType/SGA.cs @@ -1,7 +1,5 @@ -using System.Collections.Generic; using System.IO; using BinaryObjectScanner.Interfaces; -using SabreTools.Compression.zlib; namespace BinaryObjectScanner.FileType { @@ -30,147 +28,9 @@ namespace BinaryObjectScanner.FileType // Loop through and extract all files Directory.CreateDirectory(outDir); - ExtractAll(sga, outDir); + sga.ExtractAll(outDir); return true; } - - /// - /// Extract all files from the SGA to an output directory - /// - /// Output directory to write to - /// True if all files extracted, false otherwise - public static bool ExtractAll(SabreTools.Serialization.Wrappers.SGA item, string outputDirectory) - { - // Get the file count - int fileCount = item.FileCount; - if (fileCount == 0) - return false; - - // Loop through and extract all files to the output - bool allExtracted = true; - for (int i = 0; i < fileCount; i++) - { - allExtracted &= ExtractFile(item, i, outputDirectory); - } - - return allExtracted; - } - - /// - /// Extract a file from the SGA to an output directory by index - /// - /// File index to extract - /// Output directory to write to - /// True if the file extracted, false otherwise - public static bool ExtractFile(SabreTools.Serialization.Wrappers.SGA item, int index, string outputDirectory) - { - // Get the file count - int fileCount = item.FileCount; - if (fileCount == 0) - return false; - - // If the files index is invalid - if (index < 0 || index >= fileCount) - return false; - - // Create the filename - var filename = item.GetFileName(index); - if (filename == null) - return false; - - // Loop through and get all parent directories - var parentNames = new List { filename }; - - // Get the parent directory - string? folderName = item.GetParentName(index); - if (folderName != null) - parentNames.Add(folderName); - - // TODO: Should the section name/alias be used in the path as well? - - // Reverse and assemble the filename - parentNames.Reverse(); -#if NET20 || NET35 - filename = parentNames[0]; - for (int i = 1; i < parentNames.Count; i++) - { - filename = Path.Combine(filename, parentNames[i]); - } -#else - filename = Path.Combine([.. parentNames]); -#endif - - // Get and adjust the file offset - long fileOffset = item.GetFileOffset(index); - fileOffset += item.FileDataOffset; - if (fileOffset < 0) - return false; - - // Get the file sizes - long fileSize = item.GetCompressedSize(index); - long outputFileSize = item.GetUncompressedSize(index); - - // Read the compressed data directly - var compressedData = item.ReadFromDataSource((int)fileOffset, (int)fileSize); - if (compressedData == null) - return false; - - // If the compressed and uncompressed sizes match - byte[] data; - if (fileSize == outputFileSize) - { - data = compressedData; - } - else - { - // Inflate the data into the buffer - var zstream = new ZLib.z_stream_s(); - data = new byte[outputFileSize]; - unsafe - { - fixed (byte* payloadPtr = compressedData) - fixed (byte* dataPtr = data) - { - zstream.next_in = payloadPtr; - zstream.avail_in = (uint)compressedData.Length; - zstream.total_in = (uint)compressedData.Length; - zstream.next_out = dataPtr; - zstream.avail_out = (uint)data.Length; - zstream.total_out = 0; - - ZLib.inflateInit_(zstream, ZLib.zlibVersion(), compressedData.Length); - int zret = ZLib.inflate(zstream, 1); - ZLib.inflateEnd(zstream); - } - } - } - - // If we have an invalid output directory - if (string.IsNullOrEmpty(outputDirectory)) - return false; - - // Create the full output path - filename = Path.Combine(outputDirectory, filename); - - // Ensure the output directory is created - var directoryName = Path.GetDirectoryName(filename); - if (directoryName != null) - Directory.CreateDirectory(directoryName); - - // Try to write the data - try - { - // Open the output file for writing - using Stream fs = File.OpenWrite(filename); - fs.Write(data, 0, data.Length); - } - catch - { - return false; - } - - return false; - } } } diff --git a/BinaryObjectScanner/Packer/CExe.cs b/BinaryObjectScanner/Packer/CExe.cs index 90b0f44c..06aa2da2 100644 --- a/BinaryObjectScanner/Packer/CExe.cs +++ b/BinaryObjectScanner/Packer/CExe.cs @@ -110,7 +110,10 @@ namespace BinaryObjectScanner.Packer { try { - data = SabreTools.Compression.LZ.Decompressor.Decompress(payload); + var decompressor = SabreTools.Compression.SZDD.Decompressor.CreateSZDD(payload); + var dataStream = new MemoryStream(); + decompressor.CopyTo(dataStream); + data = dataStream.ToArray(); } catch { diff --git a/ExtractionTool/ExtractionTool.csproj b/ExtractionTool/ExtractionTool.csproj index 4ef1e0a0..ddc45be6 100644 --- a/ExtractionTool/ExtractionTool.csproj +++ b/ExtractionTool/ExtractionTool.csproj @@ -37,7 +37,7 @@ - + \ No newline at end of file diff --git a/ExtractionTool/Program.cs b/ExtractionTool/Program.cs index ce8887d1..1ac48d98 100644 --- a/ExtractionTool/Program.cs +++ b/ExtractionTool/Program.cs @@ -269,6 +269,42 @@ namespace ExtractionTool iscab.Extract(stream, file, outputDirectory, includeDebug: true); } + // LZ-compressed file, KWAJ variant + else if (ft == WrapperType.LZKWAJ) + { + // Build the KWAJ + Console.WriteLine("Extracting LZ-compressed file, KWAJ variant contents"); + Console.WriteLine(); + + // Extract using the FileType + var lz = new LZKWAJ(); + lz.Extract(stream, file, outputDirectory, includeDebug: true); + } + + // LZ-compressed file, QBasic variant + else if (ft == WrapperType.LZQBasic) + { + // Build the QBasic + Console.WriteLine("Extracting LZ-compressed file, QBasic variant contents"); + Console.WriteLine(); + + // Extract using the FileType + var lz = new LZQBasic(); + lz.Extract(stream, file, outputDirectory, includeDebug: true); + } + + // LZ-compressed file, SZDD variant + else if (ft == WrapperType.LZSZDD) + { + // Build the SZDD + Console.WriteLine("Extracting LZ-compressed file, SZDD variant contents"); + Console.WriteLine(); + + // Extract using the FileType + var lz = new LZSZDD(); + lz.Extract(stream, file, outputDirectory, includeDebug: true); + } + // Microsoft Cabinet archive else if (ft == WrapperType.MicrosoftCAB) { @@ -286,18 +322,6 @@ namespace ExtractionTool #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) {