From 351f749e20042e9be7eae7ce55d0f2fe6d3c6438 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Tue, 23 Apr 2024 15:12:42 -0400 Subject: [PATCH] Add ISAv3 deserializer --- .../Deserializers/InstallShieldArchiveV3.cs | 140 ++++++++++++++++++ .../Deserializers/InstallShieldCabinet.cs | 22 +-- 2 files changed, 151 insertions(+), 11 deletions(-) create mode 100644 SabreTools.Serialization/Deserializers/InstallShieldArchiveV3.cs diff --git a/SabreTools.Serialization/Deserializers/InstallShieldArchiveV3.cs b/SabreTools.Serialization/Deserializers/InstallShieldArchiveV3.cs new file mode 100644 index 00000000..1e7edfa0 --- /dev/null +++ b/SabreTools.Serialization/Deserializers/InstallShieldArchiveV3.cs @@ -0,0 +1,140 @@ +using System.Collections.Generic; +using System.IO; +using SabreTools.IO.Extensions; +using SabreTools.Models.InstallShieldArchiveV3; + +namespace SabreTools.Serialization.Deserializers +{ + public class InstallShieldArchiveV3 : BaseBinaryDeserializer + { + public override Archive? Deserialize(Stream? data) + { + // If the data is invalid + if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead) + return null; + + // If the offset is out of bounds + if (data.Position < 0 || data.Position >= data.Length) + return null; + + // Cache the current offset + int initialOffset = (int)data.Position; + + // Create a new archive to fill + var archive = new Archive(); + + #region Header + + // Try to parse the header + var header = ParseHeader(data); + if (header == null) + return null; + + // Set the archive header + archive.Header = header; + + #endregion + + #region Directories + + // Get the directories offset + uint directoriesOffset = header.TocAddress; + if (directoriesOffset < 0 || directoriesOffset >= data.Length) + return null; + + // Seek to the directories + data.Seek(directoriesOffset, SeekOrigin.Begin); + + // Try to parse the directories + var directories = new List(); + for (int i = 0; i < header.DirCount; i++) + { + var directory = ParseDirectory(data, out uint chunkSize); + if (directory?.Name == null) + return null; + + directories.Add(directory); + data.Seek(chunkSize - directory.Name.Length - 6, SeekOrigin.Current); + } + + // Set the directories + archive.Directories = [.. directories]; + + #endregion + + #region Files + + // Try to parse the files + var files = new List(); + for (int i = 0; i < archive.Directories.Length; i++) + { + var directory = archive.Directories[i]; + for (int j = 0; j < directory.FileCount; j++) + { + var file = ParseFile(data); + if (file?.Name == null) + return null; + + files.Add(file); + data.Seek(file.ChunkSize - file.Name.Length - 30, SeekOrigin.Current); + } + } + + // Set the files + archive.Files = [.. files]; + + #endregion + + return archive; + } + + /// + /// Parse a Stream into a header + /// + /// Stream to parse + /// Filled header on success, null on error + public static Header? ParseHeader(Stream data) + { + var header = data.ReadType
(); + if (header == null) + return null; + + // Invalid signature + if (header.Signature1 != 0x8C655D13) // TODO: Move constant to Models + return null; + + // Invalid TOC address + if (header.TocAddress >= data.Length) + return null; + + return header; + } + + /// + /// Parse a Stream into a directory + /// + /// Stream to parse + /// Filled directory on success, null on error + public static Models.InstallShieldArchiveV3.Directory? ParseDirectory(Stream data, out uint chunkSize) + { + // TODO: Use ReadType when model is fixed + var directory = new Models.InstallShieldArchiveV3.Directory(); + + directory.FileCount = data.ReadUInt16(); + chunkSize = data.ReadUInt16(); // TODO: Add to model and remove from output params + directory.Name = data.ReadPrefixedAnsiString(); + + return directory; + } + + /// + /// Parse a Stream into a file + /// + /// Stream to parse + /// Filled file on success, null on error + public static Models.InstallShieldArchiveV3.File? ParseFile(Stream data) + { + return data.ReadType(); + } + } +} diff --git a/SabreTools.Serialization/Deserializers/InstallShieldCabinet.cs b/SabreTools.Serialization/Deserializers/InstallShieldCabinet.cs index c1b4c680..80f3adde 100644 --- a/SabreTools.Serialization/Deserializers/InstallShieldCabinet.cs +++ b/SabreTools.Serialization/Deserializers/InstallShieldCabinet.cs @@ -411,22 +411,22 @@ namespace SabreTools.Serialization.Deserializers Descriptor descriptor = new Descriptor(); descriptor.StringsOffset = data.ReadUInt32(); - descriptor.Reserved0 = data.ReadBytes(4); + descriptor.Reserved0 = data.ReadUInt32(); descriptor.ComponentListOffset = data.ReadUInt32(); descriptor.FileTableOffset = data.ReadUInt32(); - descriptor.Reserved1 = data.ReadBytes(4); + descriptor.Reserved1 = data.ReadUInt32(); descriptor.FileTableSize = data.ReadUInt32(); descriptor.FileTableSize2 = data.ReadUInt32(); descriptor.DirectoryCount = data.ReadUInt16(); - descriptor.Reserved2 = data.ReadBytes(4); - descriptor.Reserved3 = data.ReadBytes(2); - descriptor.Reserved4 = data.ReadBytes(4); + descriptor.Reserved2 = data.ReadUInt32(); + descriptor.Reserved3 = data.ReadUInt16(); + descriptor.Reserved4 = data.ReadUInt32(); descriptor.FileCount = data.ReadUInt32(); descriptor.FileTableOffset2 = data.ReadUInt32(); descriptor.ComponentTableInfoCount = data.ReadUInt16(); descriptor.ComponentTableOffset = data.ReadUInt32(); - descriptor.Reserved5 = data.ReadBytes(4); - descriptor.Reserved6 = data.ReadBytes(4); + descriptor.Reserved5 = data.ReadUInt32(); + descriptor.Reserved6 = data.ReadUInt32(); descriptor.FileGroupOffsets = new uint[MAX_FILE_GROUP_COUNT]; for (int i = 0; i < descriptor.FileGroupOffsets.Length; i++) @@ -442,8 +442,8 @@ namespace SabreTools.Serialization.Deserializers descriptor.SetupTypesOffset = data.ReadUInt32(); descriptor.SetupTableOffset = data.ReadUInt32(); - descriptor.Reserved7 = data.ReadBytes(4); - descriptor.Reserved8 = data.ReadBytes(4); + descriptor.Reserved7 = data.ReadUInt32(); + descriptor.Reserved8 = data.ReadUInt32(); return descriptor; } @@ -471,9 +471,9 @@ namespace SabreTools.Serialization.Deserializers // Read the string if (majorVersion >= 17) - offsetList.Name = data.ReadString(Encoding.Unicode); + offsetList.Name = data.ReadNullTerminatedUnicodeString(); else - offsetList.Name = data.ReadString(Encoding.ASCII); + offsetList.Name = data.ReadNullTerminatedAnsiString(); // Seek back to the correct offset data.Seek(currentOffset, SeekOrigin.Begin);