diff --git a/SabreTools.Serialization/UnshieldSharp/Extractor.cs b/SabreTools.Serialization/UnshieldSharp/Extractor.cs
deleted file mode 100644
index bed9ef7f..00000000
--- a/SabreTools.Serialization/UnshieldSharp/Extractor.cs
+++ /dev/null
@@ -1,232 +0,0 @@
-using System;
-using System.IO;
-using SabreTools.Hashing;
-using SabreTools.IO.Compression.zlib;
-using SabreTools.Models.InstallShieldCabinet;
-using Header = SabreTools.Serialization.Wrappers.InstallShieldCabinet;
-
-namespace UnshieldSharpInternal
-{
- // TODO: Figure out if individual parts of a split cab can be extracted separately
- internal class Extractor
- {
- ///
- /// Linked CAB headers
- ///
- public Header HeaderList { get; }
-
- ///
- /// Default buffer size
- ///
- private const int BUFFER_SIZE = 64 * 1024;
-
- #region Constructors
-
- public Extractor(Header headerList)
- {
- HeaderList = headerList;
- }
-
- #endregion
-
- #region File
-
- ///
- /// Save the file at the given index to the filename specified
- ///
- public bool FileSave(int index, string filename, bool useOld = false)
- {
- // Get the file descriptor
- if (!HeaderList.TryGetFileDescriptor(index, out var fileDescriptor) || fileDescriptor == null)
- return false;
-
- // If the file is split
- if (fileDescriptor.LinkFlags == LinkFlags.LINK_PREV)
- return FileSave((int)fileDescriptor.LinkPrevious, filename, useOld);
-
- // Get the reader at the index
- var reader = Reader.Create(this, index, fileDescriptor);
- if (reader == null)
- return false;
-
- // Create the output file and hasher
- FileStream output = File.OpenWrite(filename);
- var md5 = new HashWrapper(HashType.MD5);
-
- ulong bytesLeft = Header.GetReadableBytes(fileDescriptor);
- byte[] inputBuffer;
- byte[] outputBuffer = new byte[BUFFER_SIZE];
- ulong totalWritten = 0;
-
- // Read while there are bytes remaining
- while (bytesLeft > 0)
- {
- ulong bytesToWrite = BUFFER_SIZE;
- int result;
-
- // Handle compressed files
-#if NET20 || NET35
- if ((fileDescriptor.Flags & FileFlags.FILE_COMPRESSED) != 0)
-#else
- if (fileDescriptor.Flags.HasFlag(FileFlags.FILE_COMPRESSED))
-#endif
- {
- // Attempt to read the length value
- byte[] lengthArr = new byte[sizeof(ushort)];
- if (!reader.Read(lengthArr, 0, lengthArr.Length))
- {
- Console.Error.WriteLine($"Failed to read {lengthArr.Length} bytes of file {index} ({HeaderList.GetFileName(index)}) from input cabinet file {fileDescriptor.Volume}");
- reader.Dispose();
- output?.Close();
- return false;
- }
-
- // Validate the number of bytes to read
- ushort bytesToRead = BitConverter.ToUInt16(lengthArr, 0);
- if (bytesToRead == 0)
- {
- Console.Error.WriteLine("bytesToRead can't be zero");
- reader.Dispose();
- output?.Close();
- return false;
- }
-
- // Attempt to read the specified number of bytes
- inputBuffer = new byte[BUFFER_SIZE + 1];
- if (!reader.Read(inputBuffer, 0, bytesToRead))
- {
- Console.Error.WriteLine($"Failed to read {lengthArr.Length} bytes of file {index} ({HeaderList.GetFileName(index)}) from input cabinet file {fileDescriptor.Volume}");
- reader.Dispose();
- output?.Close();
- return false;
- }
-
- // Add a null byte to make inflate happy
- inputBuffer[bytesToRead] = 0;
- ulong readBytes = (ulong)(bytesToRead + 1);
-
- // Uncompress into a buffer
- if (useOld)
- result = Header.UncompressOld(outputBuffer, ref bytesToWrite, inputBuffer, ref readBytes);
- else
- result = Header.Uncompress(outputBuffer, ref bytesToWrite, inputBuffer, ref readBytes);
-
- // If we didn't get a positive result that's not a data error (false positives)
- if (result != zlibConst.Z_OK && result != zlibConst.Z_DATA_ERROR)
- {
- Console.Error.WriteLine($"Decompression failed with code {result.ToZlibConstName()}. bytes_to_read={bytesToRead}, volume={fileDescriptor.Volume}, read_bytes={readBytes}");
- reader.Dispose();
- output?.Close();
- return false;
- }
-
- // Set remaining bytes
- bytesLeft -= 2;
- bytesLeft -= bytesToRead;
- }
-
- // Handle uncompressed files
- else
- {
- bytesToWrite = Math.Min(bytesLeft, BUFFER_SIZE);
- if (!reader.Read(outputBuffer, 0, (int)bytesToWrite))
- {
- Console.Error.WriteLine($"Failed to write {bytesToWrite} bytes from input cabinet file {fileDescriptor.Volume}");
- reader.Dispose();
- output?.Close();
- return false;
- }
-
- // Set remaining bytes
- bytesLeft -= (uint)bytesToWrite;
- }
-
- // Hash and write the next block
- md5.Process(outputBuffer, 0, (int)bytesToWrite);
- output?.Write(outputBuffer, 0, (int)bytesToWrite);
- totalWritten += bytesToWrite;
- }
-
- // Validate the number of bytes written
- if (fileDescriptor.ExpandedSize != totalWritten)
- {
- Console.Error.WriteLine($"Expanded size expected to be {fileDescriptor.ExpandedSize}, but was {totalWritten}");
- reader.Dispose();
- output?.Close();
- return false;
- }
-
- // Finalize output values
- md5.Terminate();
- reader?.Dispose();
- output?.Close();
-
- // Failing the file has been disabled because for a subset of CABs the values don't seem to match
- // TODO: Investigate what is causing this to fail and what data needs to be hashed
-
- // // Validate the data written, if required
- // if (HeaderList!.MajorVersion >= 6)
- // {
- // string? md5result = md5.CurrentHashString;
- // if (md5result == null || md5result != BitConverter.ToString(fileDescriptor.MD5!))
- // {
- // Console.Error.WriteLine($"MD5 checksum failure for file {index} ({HeaderList.GetFileName(index)})");
- // return false;
- // }
- // }
-
- return true;
- }
-
- ///
- /// Save the file at the given index to the filename specified as raw
- ///
- public bool FileSaveRaw(int index, string filename)
- {
- // Get the file descriptor
- if (!HeaderList.TryGetFileDescriptor(index, out var fileDescriptor) || fileDescriptor == null)
- return false;
-
- // If the file is split
- if (fileDescriptor.LinkFlags == LinkFlags.LINK_PREV)
- return FileSaveRaw((int)fileDescriptor.LinkPrevious, filename);
-
- // Get the reader at the index
- var reader = Reader.Create(this, index, fileDescriptor);
- if (reader == null)
- return false;
-
- // Create the output file
- FileStream output = File.OpenWrite(filename);
-
- ulong bytesLeft = Header.GetReadableBytes(fileDescriptor);
- byte[] outputBuffer = new byte[BUFFER_SIZE];
-
- // Read while there are bytes remaining
- while (bytesLeft > 0)
- {
- ulong bytesToWrite = Math.Min(bytesLeft, BUFFER_SIZE);
- if (!reader.Read(outputBuffer, 0, (int)bytesToWrite))
- {
- Console.Error.WriteLine($"Failed to read {bytesToWrite} bytes from input cabinet file {fileDescriptor.Volume}");
- reader.Dispose();
- output?.Close();
- return false;
- }
-
- // Set remaining bytes
- bytesLeft -= (uint)bytesToWrite;
-
- // Write the next block
- output.Write(outputBuffer, 0, (int)bytesToWrite);
- }
-
- // Finalize output values
- reader.Dispose();
- output?.Close();
- return true;
- }
-
- #endregion
- }
-}
diff --git a/SabreTools.Serialization/UnshieldSharp/Reader.cs b/SabreTools.Serialization/UnshieldSharp/Reader.cs
index fd002c96..4f1144af 100644
--- a/SabreTools.Serialization/UnshieldSharp/Reader.cs
+++ b/SabreTools.Serialization/UnshieldSharp/Reader.cs
@@ -1,6 +1,7 @@
using System;
using System.IO;
using SabreTools.Models.InstallShieldCabinet;
+using Header = SabreTools.Serialization.Wrappers.InstallShieldCabinet;
namespace UnshieldSharpInternal
{
@@ -11,7 +12,7 @@ namespace UnshieldSharpInternal
///
/// Cabinet file to read from
///
- private readonly Extractor _extractor;
+ private readonly Header _cabinet;
///
/// Currently selected index
@@ -57,9 +58,9 @@ namespace UnshieldSharpInternal
#region Constructors
- private Reader(Extractor cabinet, uint index, FileDescriptor fileDescriptor)
+ private Reader(Header cabinet, uint index, FileDescriptor fileDescriptor)
{
- _extractor = cabinet;
+ _cabinet = cabinet;
_index = index;
_fileDescriptor = fileDescriptor;
}
@@ -69,7 +70,7 @@ namespace UnshieldSharpInternal
///
/// Create a new from an existing cabinet, index, and file descriptor
///
- public static Reader? Create(Extractor cabinet, int index, FileDescriptor fileDescriptor)
+ public static Reader? Create(Header cabinet, int index, FileDescriptor fileDescriptor)
{
var reader = new Reader(cabinet, (uint)index, fileDescriptor);
for (; ; )
@@ -87,7 +88,7 @@ namespace UnshieldSharpInternal
}
// Start with the correct volume for IS5 cabinets
- if (reader._extractor.HeaderList.MajorVersion <= 5 && index > (int)reader._volumeHeader.LastFileIndex)
+ if (reader._cabinet.MajorVersion <= 5 && index > (int)reader._volumeHeader.LastFileIndex)
{
// Normalize the volume ID for odd cases
if (fileDescriptor.Volume == ushort.MinValue || fileDescriptor.Volume == ushort.MaxValue)
@@ -147,7 +148,7 @@ namespace UnshieldSharpInternal
#else
if (_fileDescriptor.Flags.HasFlag(FileFlags.FILE_OBFUSCATED))
#endif
- SabreTools.Serialization.Wrappers.InstallShieldCabinet.Deobfuscate(buffer, size, ref _obfuscationOffset);
+ Header.Deobfuscate(buffer, size, ref _obfuscationOffset);
return true;
}
@@ -167,7 +168,7 @@ namespace UnshieldSharpInternal
private bool OpenVolume(ushort volume)
{
// Read the volume from the cabinet set
- var next = _extractor.HeaderList.OpenVolume(volume, out var volumeStream);
+ var next = _cabinet.OpenVolume(volume, out var volumeStream);
if (next?.VolumeHeader == null || volumeStream == null)
{
Console.Error.WriteLine($"Failed to open input cabinet file {volume}");
@@ -180,9 +181,9 @@ namespace UnshieldSharpInternal
_volumeHeader = next.VolumeHeader;
// Enable support for split archives for IS5
- if (_extractor.HeaderList.MajorVersion == 5)
+ if (_cabinet.MajorVersion == 5)
{
- if (_index < (_extractor.HeaderList.FileCount - 1)
+ if (_index < (_cabinet.FileCount - 1)
&& _index == _volumeHeader.LastFileIndex
&& _volumeHeader.LastFileSizeCompressed != _fileDescriptor.CompressedSize)
{
diff --git a/SabreTools.Serialization/Wrappers/InstallShieldCabinet.cs b/SabreTools.Serialization/Wrappers/InstallShieldCabinet.cs
index 1b31dc99..2fff04a3 100644
--- a/SabreTools.Serialization/Wrappers/InstallShieldCabinet.cs
+++ b/SabreTools.Serialization/Wrappers/InstallShieldCabinet.cs
@@ -1,9 +1,11 @@
using System;
using System.IO;
using System.Text.RegularExpressions;
+using SabreTools.Hashing;
using SabreTools.IO.Compression.zlib;
using SabreTools.Models.InstallShieldCabinet;
using SabreTools.Serialization.Interfaces;
+using UnshieldSharpInternal;
using static SabreTools.Models.InstallShieldCabinet.Constants;
namespace SabreTools.Serialization.Wrappers
@@ -404,7 +406,6 @@ namespace SabreTools.Serialization.Wrappers
try
{
- var cabfile = new UnshieldSharpInternal.Extractor(cabinet);
for (int i = 0; i < cabinet.FileCount; i++)
{
try
@@ -426,7 +427,7 @@ namespace SabreTools.Serialization.Wrappers
if (directoryName != null && !Directory.Exists(directoryName))
Directory.CreateDirectory(directoryName);
- cabfile.FileSave(i, filename);
+ cabinet.FileSave(i, filename);
}
catch (Exception ex)
{
@@ -443,10 +444,206 @@ namespace SabreTools.Serialization.Wrappers
}
}
+ ///
+ /// Save the file at the given index to the filename specified
+ ///
+ public bool FileSave(int index, string filename, bool useOld = false)
+ {
+ // Get the file descriptor
+ if (!TryGetFileDescriptor(index, out var fileDescriptor) || fileDescriptor == null)
+ return false;
+
+ // If the file is split
+ if (fileDescriptor.LinkFlags == LinkFlags.LINK_PREV)
+ return FileSave((int)fileDescriptor.LinkPrevious, filename, useOld);
+
+ // Get the reader at the index
+ var reader = Reader.Create(this, index, fileDescriptor);
+ if (reader == null)
+ return false;
+
+ // Create the output file and hasher
+ FileStream output = File.OpenWrite(filename);
+ var md5 = new HashWrapper(HashType.MD5);
+
+ ulong bytesLeft = GetReadableBytes(fileDescriptor);
+ byte[] inputBuffer;
+ byte[] outputBuffer = new byte[BUFFER_SIZE];
+ ulong totalWritten = 0;
+
+ // Read while there are bytes remaining
+ while (bytesLeft > 0)
+ {
+ ulong bytesToWrite = BUFFER_SIZE;
+ int result;
+
+ // Handle compressed files
+#if NET20 || NET35
+ if ((fileDescriptor.Flags & FileFlags.FILE_COMPRESSED) != 0)
+#else
+ if (fileDescriptor.Flags.HasFlag(FileFlags.FILE_COMPRESSED))
+#endif
+ {
+ // Attempt to read the length value
+ byte[] lengthArr = new byte[sizeof(ushort)];
+ if (!reader.Read(lengthArr, 0, lengthArr.Length))
+ {
+ Console.Error.WriteLine($"Failed to read {lengthArr.Length} bytes of file {index} ({GetFileName(index)}) from input cabinet file {fileDescriptor.Volume}");
+ reader.Dispose();
+ output?.Close();
+ return false;
+ }
+
+ // Validate the number of bytes to read
+ ushort bytesToRead = BitConverter.ToUInt16(lengthArr, 0);
+ if (bytesToRead == 0)
+ {
+ Console.Error.WriteLine("bytesToRead can't be zero");
+ reader.Dispose();
+ output?.Close();
+ return false;
+ }
+
+ // Attempt to read the specified number of bytes
+ inputBuffer = new byte[BUFFER_SIZE + 1];
+ if (!reader.Read(inputBuffer, 0, bytesToRead))
+ {
+ Console.Error.WriteLine($"Failed to read {lengthArr.Length} bytes of file {index} ({GetFileName(index)}) from input cabinet file {fileDescriptor.Volume}");
+ reader.Dispose();
+ output?.Close();
+ return false;
+ }
+
+ // Add a null byte to make inflate happy
+ inputBuffer[bytesToRead] = 0;
+ ulong readBytes = (ulong)(bytesToRead + 1);
+
+ // Uncompress into a buffer
+ if (useOld)
+ result = UncompressOld(outputBuffer, ref bytesToWrite, inputBuffer, ref readBytes);
+ else
+ result = Uncompress(outputBuffer, ref bytesToWrite, inputBuffer, ref readBytes);
+
+ // If we didn't get a positive result that's not a data error (false positives)
+ if (result != zlibConst.Z_OK && result != zlibConst.Z_DATA_ERROR)
+ {
+ Console.Error.WriteLine($"Decompression failed with code {result.ToZlibConstName()}. bytes_to_read={bytesToRead}, volume={fileDescriptor.Volume}, read_bytes={readBytes}");
+ reader.Dispose();
+ output?.Close();
+ return false;
+ }
+
+ // Set remaining bytes
+ bytesLeft -= 2;
+ bytesLeft -= bytesToRead;
+ }
+
+ // Handle uncompressed files
+ else
+ {
+ bytesToWrite = Math.Min(bytesLeft, BUFFER_SIZE);
+ if (!reader.Read(outputBuffer, 0, (int)bytesToWrite))
+ {
+ Console.Error.WriteLine($"Failed to write {bytesToWrite} bytes from input cabinet file {fileDescriptor.Volume}");
+ reader.Dispose();
+ output?.Close();
+ return false;
+ }
+
+ // Set remaining bytes
+ bytesLeft -= (uint)bytesToWrite;
+ }
+
+ // Hash and write the next block
+ md5.Process(outputBuffer, 0, (int)bytesToWrite);
+ output?.Write(outputBuffer, 0, (int)bytesToWrite);
+ totalWritten += bytesToWrite;
+ }
+
+ // Validate the number of bytes written
+ if (fileDescriptor.ExpandedSize != totalWritten)
+ {
+ Console.Error.WriteLine($"Expanded size expected to be {fileDescriptor.ExpandedSize}, but was {totalWritten}");
+ reader.Dispose();
+ output?.Close();
+ return false;
+ }
+
+ // Finalize output values
+ md5.Terminate();
+ reader?.Dispose();
+ output?.Close();
+
+ // Failing the file has been disabled because for a subset of CABs the values don't seem to match
+ // TODO: Investigate what is causing this to fail and what data needs to be hashed
+
+ // // Validate the data written, if required
+ // if (HeaderList!.MajorVersion >= 6)
+ // {
+ // string? md5result = md5.CurrentHashString;
+ // if (md5result == null || md5result != BitConverter.ToString(fileDescriptor.MD5!))
+ // {
+ // Console.Error.WriteLine($"MD5 checksum failure for file {index} ({HeaderList.GetFileName(index)})");
+ // return false;
+ // }
+ // }
+
+ return true;
+ }
+
+ ///
+ /// Save the file at the given index to the filename specified as raw
+ ///
+ public bool FileSaveRaw(int index, string filename)
+ {
+ // Get the file descriptor
+ if (!TryGetFileDescriptor(index, out var fileDescriptor) || fileDescriptor == null)
+ return false;
+
+ // If the file is split
+ if (fileDescriptor.LinkFlags == LinkFlags.LINK_PREV)
+ return FileSaveRaw((int)fileDescriptor.LinkPrevious, filename);
+
+ // Get the reader at the index
+ var reader = Reader.Create(this, index, fileDescriptor);
+ if (reader == null)
+ return false;
+
+ // Create the output file
+ FileStream output = File.OpenWrite(filename);
+
+ ulong bytesLeft = GetReadableBytes(fileDescriptor);
+ byte[] outputBuffer = new byte[BUFFER_SIZE];
+
+ // Read while there are bytes remaining
+ while (bytesLeft > 0)
+ {
+ ulong bytesToWrite = Math.Min(bytesLeft, BUFFER_SIZE);
+ if (!reader.Read(outputBuffer, 0, (int)bytesToWrite))
+ {
+ Console.Error.WriteLine($"Failed to read {bytesToWrite} bytes from input cabinet file {fileDescriptor.Volume}");
+ reader.Dispose();
+ output?.Close();
+ return false;
+ }
+
+ // Set remaining bytes
+ bytesLeft -= (uint)bytesToWrite;
+
+ // Write the next block
+ output.Write(outputBuffer, 0, (int)bytesToWrite);
+ }
+
+ // Finalize output values
+ reader.Dispose();
+ output?.Close();
+ return true;
+ }
+
///
/// Uncompress a source byte array to a destination
///
- public unsafe static int Uncompress(byte[] dest, ref ulong destLen, byte[] source, ref ulong sourceLen)
+ private unsafe static int Uncompress(byte[] dest, ref ulong destLen, byte[] source, ref ulong sourceLen)
{
fixed (byte* sourcePtr = source)
fixed (byte* destPtr = dest)
@@ -480,7 +677,7 @@ namespace SabreTools.Serialization.Wrappers
///
/// Uncompress a source byte array to a destination (old version)
///
- public unsafe static int UncompressOld(byte[] dest, ref ulong destLen, byte[] source, ref ulong sourceLen)
+ private unsafe static int UncompressOld(byte[] dest, ref ulong destLen, byte[] source, ref ulong sourceLen)
{
fixed (byte* sourcePtr = source)
fixed (byte* destPtr = dest)