diff --git a/ExtractionTool/Program.cs b/ExtractionTool/Program.cs index 46bcdfe2..684855ec 100644 --- a/ExtractionTool/Program.cs +++ b/ExtractionTool/Program.cs @@ -1,6 +1,5 @@ using System; using System.IO; -using System.Text.RegularExpressions; using SabreTools.IO.Extensions; using SabreTools.Serialization.Wrappers; @@ -140,9 +139,8 @@ namespace ExtractionTool break; // IS-CAB archive - case InstallShieldCabinet: - // TODO: Move this handling to Serialization directly - ExtractInstallShieldCabinet(file, outputDirectory, includeDebug); + case InstallShieldCabinet iscab: + iscab.Extract(outputDirectory, includeDebug); break; // LZ-compressed file, KWAJ variant @@ -247,85 +245,5 @@ namespace ExtractionTool break; } } - - /// - /// Handle IS-CAB archives - /// - private static bool ExtractInstallShieldCabinet(string file, string outputDirectory, bool includeDebug) - { - // Get the name of the first cabinet file or header - var directory = Path.GetDirectoryName(file); - string noExtension = Path.GetFileNameWithoutExtension(file); - - bool shouldScanCabinet; - if (directory == null) - { - string filenamePattern = noExtension; - filenamePattern = new Regex(@"\d+$").Replace(filenamePattern, string.Empty); - bool cabinetHeaderExists = File.Exists(filenamePattern + "1.hdr"); - shouldScanCabinet = cabinetHeaderExists - ? file.Equals(filenamePattern + "1.hdr", StringComparison.OrdinalIgnoreCase) - : file.Equals(filenamePattern + "1.cab", StringComparison.OrdinalIgnoreCase); - } - else - { - string filenamePattern = Path.Combine(directory, noExtension); - filenamePattern = new Regex(@"\d+$").Replace(filenamePattern, string.Empty); - bool cabinetHeaderExists = File.Exists(Path.Combine(directory, filenamePattern + "1.hdr")); - shouldScanCabinet = cabinetHeaderExists - ? file.Equals(Path.Combine(directory, filenamePattern + "1.hdr"), StringComparison.OrdinalIgnoreCase) - : file.Equals(Path.Combine(directory, filenamePattern + "1.cab"), StringComparison.OrdinalIgnoreCase); - } - - // If we have anything but the first file - if (!shouldScanCabinet) - return false; - - try - { - if (!File.Exists(file)) - return false; - - var cabfile = UnshieldSharpInternal.InstallShieldCabinet.Open(file); - if (cabfile?.HeaderList == null) - return false; - - for (int i = 0; i < cabfile.HeaderList.FileCount; i++) - { - try - { - // Check if the file is valid first - if (!cabfile.HeaderList.FileIsValid(i)) - continue; - - // Ensure directory separators are consistent - string filename = cabfile.HeaderList.GetFileName(i) ?? $"BAD_FILENAME{i}"; - if (Path.DirectorySeparatorChar == '\\') - filename = filename.Replace('/', '\\'); - else if (Path.DirectorySeparatorChar == '/') - filename = filename.Replace('\\', '/'); - - // Ensure the full output directory exists - filename = Path.Combine(outputDirectory, filename); - var directoryName = Path.GetDirectoryName(filename); - if (directoryName != null && !Directory.Exists(directoryName)) - Directory.CreateDirectory(directoryName); - - cabfile.FileSave(i, filename); - } - catch (Exception ex) - { - if (includeDebug) Console.Error.WriteLine(ex); - } - } - - return true; - } - catch (Exception ex) - { - if (includeDebug) Console.Error.WriteLine(ex); - return false; - } - } } } diff --git a/SabreTools.Serialization/SabreTools.Serialization.csproj b/SabreTools.Serialization/SabreTools.Serialization.csproj index 772b15df..8c232075 100644 --- a/SabreTools.Serialization/SabreTools.Serialization.csproj +++ b/SabreTools.Serialization/SabreTools.Serialization.csproj @@ -27,7 +27,6 @@ - diff --git a/SabreTools.Serialization/UnshieldSharp/InstallShieldCabinet.cs b/SabreTools.Serialization/UnshieldSharp/InstallShieldCabinet.cs index 9b5e6b9e..5bc76c47 100644 --- a/SabreTools.Serialization/UnshieldSharp/InstallShieldCabinet.cs +++ b/SabreTools.Serialization/UnshieldSharp/InstallShieldCabinet.cs @@ -13,48 +13,24 @@ namespace UnshieldSharpInternal /// /// Linked CAB headers /// - public Header? HeaderList { get; private set; } + public Header HeaderList { get; private set; } /// /// Base filename path for related CAB files /// - public string? FilenamePattern { get; private set; } + public string FilenamePattern { get; private set; } /// /// Default buffer size /// private const int BUFFER_SIZE = 64 * 1024; - /// - /// Maximum size of the window in bits - /// - /// TODO: Remove when Serialization is updated - private const int MAX_WBITS = 15; + #region Constructors - #region Open Cabinet - - /// - /// Open a file as an InstallShield CAB - /// - public static InstallShieldCabinet? Open(string filename) + public InstallShieldCabinet(string filenamePattern, Header headerList) { - var cabinet = new InstallShieldCabinet(); - - cabinet.FilenamePattern = Header.CreateFilenamePattern(filename); - if (cabinet.FilenamePattern == null) - { - Console.Error.WriteLine("Failed to create filename pattern"); - return null; - } - - cabinet.HeaderList = Header.OpenSet(cabinet.FilenamePattern); - if (cabinet.HeaderList == null) - { - Console.Error.WriteLine("Failed to read header files"); - return null; - } - - return cabinet; + FilenamePattern = filenamePattern; + HeaderList = headerList; } #endregion @@ -66,12 +42,6 @@ namespace UnshieldSharpInternal /// public bool FileSave(int index, string filename, bool useOld = false) { - if (HeaderList == null) - { - Console.Error.WriteLine("Header list is not built"); - return false; - } - // Get the file descriptor if (!HeaderList.TryGetFileDescriptor(index, out var fileDescriptor) || fileDescriptor == null) return false; @@ -219,12 +189,6 @@ namespace UnshieldSharpInternal /// public bool FileSaveRaw(int index, string filename) { - if (HeaderList == null) - { - Console.Error.WriteLine("Header list is not built"); - return false; - } - // Get the file descriptor if (!HeaderList.TryGetFileDescriptor(index, out var fileDescriptor) || fileDescriptor == null) return false; diff --git a/SabreTools.Serialization/UnshieldSharp/Reader.cs b/SabreTools.Serialization/UnshieldSharp/Reader.cs index 16f13423..8cfba16e 100644 --- a/SabreTools.Serialization/UnshieldSharp/Reader.cs +++ b/SabreTools.Serialization/UnshieldSharp/Reader.cs @@ -13,17 +13,17 @@ namespace UnshieldSharpInternal /// /// Cabinet file to read from /// - private InstallShieldCabinet? _cabinet; + private readonly InstallShieldCabinet _cabinet; /// /// Currently selected index /// - private uint _index; + private readonly uint _index; /// /// File descriptor defining the currently selected index /// - private FileDescriptor? _fileDescriptor; + private readonly FileDescriptor _fileDescriptor; /// /// Number of bytes left in the current volume @@ -52,25 +52,23 @@ namespace UnshieldSharpInternal #endregion + #region Constructors + + private Reader(InstallShieldCabinet cabinet, uint index, FileDescriptor fileDescriptor) + { + _cabinet = cabinet; + _index = index; + _fileDescriptor = fileDescriptor; + } + + #endregion + /// /// Create a new from an existing cabinet, index, and file descriptor /// public static Reader? Create(InstallShieldCabinet cabinet, int index, FileDescriptor fileDescriptor) { - var reader = new Reader - { - _cabinet = cabinet, - _index = (uint)index, - _fileDescriptor = fileDescriptor, - }; - - // If the cabinet header list is invalid - if (reader._cabinet.HeaderList == null) - { - Console.Error.WriteLine($"Header list is invalid"); - return null; - } - + var reader = new Reader(cabinet, (uint)index, fileDescriptor); for (; ; ) { // If the volume is invalid @@ -151,9 +149,9 @@ namespace UnshieldSharpInternal } #if NET20 || NET35 - if ((_fileDescriptor!.Flags & FileFlags.FILE_OBFUSCATED) != 0) + if ((_fileDescriptor.Flags & FileFlags.FILE_OBFUSCATED) != 0) #else - if (_fileDescriptor!.Flags.HasFlag(FileFlags.FILE_OBFUSCATED)) + if (_fileDescriptor.Flags.HasFlag(FileFlags.FILE_OBFUSCATED)) #endif SabreTools.Serialization.Wrappers.InstallShieldCabinet.Deobfuscate(buffer, size, ref _obfuscationOffset); @@ -170,7 +168,7 @@ namespace UnshieldSharpInternal volume = 1; _volumeFile?.Close(); - _volumeFile = SabreTools.Serialization.Wrappers.InstallShieldCabinet.OpenFileForReading(_cabinet!.FilenamePattern, volume, CABINET_SUFFIX); + _volumeFile = SabreTools.Serialization.Wrappers.InstallShieldCabinet.OpenFileForReading(_cabinet.FilenamePattern, volume, CABINET_SUFFIX); if (_volumeFile == null) { Console.Error.WriteLine($"Failed to open input cabinet file {volume}"); @@ -181,7 +179,7 @@ namespace UnshieldSharpInternal if (commonHeader == default) return false; - _volumeHeader = SabreTools.Serialization.Deserializers.InstallShieldCabinet.ParseVolumeHeader(_volumeFile, _cabinet.HeaderList!.MajorVersion); + _volumeHeader = SabreTools.Serialization.Deserializers.InstallShieldCabinet.ParseVolumeHeader(_volumeFile, _cabinet.HeaderList.MajorVersion); if (_volumeHeader == null) return false; @@ -190,13 +188,13 @@ namespace UnshieldSharpInternal { if (_index < (_cabinet.HeaderList.FileCount - 1) && _index == _volumeHeader.LastFileIndex - && _volumeHeader.LastFileSizeCompressed != _fileDescriptor!.CompressedSize) + && _volumeHeader.LastFileSizeCompressed != _fileDescriptor.CompressedSize) { _fileDescriptor.Flags |= FileFlags.FILE_SPLIT; } else if (_index > 0 && _index == _volumeHeader.FirstFileIndex - && _volumeHeader.FirstFileSizeCompressed != _fileDescriptor!.CompressedSize) + && _volumeHeader.FirstFileSizeCompressed != _fileDescriptor.CompressedSize) { _fileDescriptor.Flags |= FileFlags.FILE_SPLIT; } @@ -204,9 +202,9 @@ namespace UnshieldSharpInternal ulong dataOffset, volumeBytesLeftCompressed, volumeBytesLeftExpanded; #if NET20 || NET35 - if ((_fileDescriptor!.Flags & FileFlags.FILE_SPLIT) != 0) + if ((_fileDescriptor.Flags & FileFlags.FILE_SPLIT) != 0) #else - if (_fileDescriptor!.Flags.HasFlag(FileFlags.FILE_SPLIT)) + if (_fileDescriptor.Flags.HasFlag(FileFlags.FILE_SPLIT)) #endif { if (_index == _volumeHeader.LastFileIndex && _volumeHeader.LastFileOffset != 0x7FFFFFFF) diff --git a/SabreTools.Serialization/Wrappers/InstallShieldCabinet.cs b/SabreTools.Serialization/Wrappers/InstallShieldCabinet.cs index 089df050..60904b6b 100644 --- a/SabreTools.Serialization/Wrappers/InstallShieldCabinet.cs +++ b/SabreTools.Serialization/Wrappers/InstallShieldCabinet.cs @@ -3,11 +3,12 @@ using System.IO; using System.Text.RegularExpressions; using SabreTools.IO.Compression.zlib; using SabreTools.Models.InstallShieldCabinet; +using SabreTools.Serialization.Interfaces; using static SabreTools.Models.InstallShieldCabinet.Constants; namespace SabreTools.Serialization.Wrappers { - public partial class InstallShieldCabinet : WrapperBase + public partial class InstallShieldCabinet : WrapperBase, IExtractable { #region Descriptive Properties @@ -353,6 +354,74 @@ namespace SabreTools.Serialization.Wrappers #region Extraction + /// + public bool Extract(string outputDirectory, bool includeDebug) + { + // Open the full set if possible + var cabinet = this; + string pattern = string.Empty; + if (Filename != null) + { + // Get the name of the first cabinet file or header + pattern = CreateFilenamePattern(Filename)!; + bool cabinetHeaderExists = File.Exists(pattern + "1.hdr"); + bool shouldScanCabinet = cabinetHeaderExists + ? Filename.Equals(pattern + "1.hdr", StringComparison.OrdinalIgnoreCase) + : Filename.Equals(pattern + "1.cab", StringComparison.OrdinalIgnoreCase); + + // If we have anything but the first file + if (!shouldScanCabinet) + return false; + + // Open the set from the pattern + cabinet = OpenSet(pattern); + } + + // If the cabinet set could not be opened + if (cabinet == null) + return false; + + try + { + var cabfile = new UnshieldSharpInternal.InstallShieldCabinet(pattern, cabinet); + for (int i = 0; i < cabinet.FileCount; i++) + { + try + { + // Check if the file is valid first + if (!cabinet.FileIsValid(i)) + continue; + + // Ensure directory separators are consistent + string filename = cabinet.GetFileName(i) ?? $"BAD_FILENAME{i}"; + if (Path.DirectorySeparatorChar == '\\') + filename = filename.Replace('/', '\\'); + else if (Path.DirectorySeparatorChar == '/') + filename = filename.Replace('\\', '/'); + + // Ensure the full output directory exists + filename = Path.Combine(outputDirectory, filename); + var directoryName = Path.GetDirectoryName(filename); + if (directoryName != null && !Directory.Exists(directoryName)) + Directory.CreateDirectory(directoryName); + + cabfile.FileSave(i, filename); + } + catch (Exception ex) + { + if (includeDebug) Console.Error.WriteLine(ex); + } + } + + return true; + } + catch (Exception ex) + { + if (includeDebug) Console.Error.WriteLine(ex); + return false; + } + } + /// /// Uncompress a source byte array to a destination ///