From 70468b72c35056239a4785c3a9af2f4ed889cddd Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Fri, 13 Jan 2023 21:40:01 -0800 Subject: [PATCH] Fix up IS-CAB a little --- BurnOutSharp.Builders/InstallShieldCabinet.cs | 181 +++++++++++------- .../InstallShieldCabinet/Cabinet.cs | 75 ++++++++ .../InstallShieldCabinet/Header.cs | 63 ------ 3 files changed, 184 insertions(+), 135 deletions(-) create mode 100644 BurnOutSharp.Models/InstallShieldCabinet/Cabinet.cs delete mode 100644 BurnOutSharp.Models/InstallShieldCabinet/Header.cs diff --git a/BurnOutSharp.Builders/InstallShieldCabinet.cs b/BurnOutSharp.Builders/InstallShieldCabinet.cs index a18a4673..a7c84fff 100644 --- a/BurnOutSharp.Builders/InstallShieldCabinet.cs +++ b/BurnOutSharp.Builders/InstallShieldCabinet.cs @@ -18,7 +18,7 @@ namespace BurnOutSharp.Builders /// Byte array to parse /// Offset into the byte array /// Filled cabinet on success, null on error - public static Header ParseCabinet(byte[] data, int offset) + public static Cabinet ParseCabinet(byte[] data, int offset) { // If the data is invalid if (data == null) @@ -42,7 +42,7 @@ namespace BurnOutSharp.Builders /// /// Stream to parse /// Filled cabinet on success, null on error - public static Header ParseCabinet(Stream data) + public static Cabinet ParseCabinet(Stream data) { // If the data is invalid if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead) @@ -56,7 +56,7 @@ namespace BurnOutSharp.Builders int initialOffset = (int)data.Position; // Create a new cabinet to fill - var header = new Header(); + var cabinet = new Cabinet(); #region Common Header @@ -66,7 +66,19 @@ namespace BurnOutSharp.Builders return null; // Set the cabinet header - header.CommonHeader = commonHeader; + cabinet.CommonHeader = commonHeader; + + #endregion + + #region Volume Header + + // Try to parse the volume header + var volumeHeader = ParseVolumeHeader(data, GetMajorVersion(commonHeader)); + if (volumeHeader == null) + return null; + + // Set the volume header + cabinet.VolumeHeader = volumeHeader; #endregion @@ -86,7 +98,7 @@ namespace BurnOutSharp.Builders return null; // Set the cabinet descriptor - header.CabinetDescriptor = cabinetDescriptor; + cabinet.CabinetDescriptor = cabinetDescriptor; #endregion @@ -102,16 +114,16 @@ namespace BurnOutSharp.Builders // Get the number of file table items uint fileTableItems; - if (header.MajorVersion <= 5) + if (GetMajorVersion(commonHeader) <= 5) fileTableItems = cabinetDescriptor.DirectoryCount + cabinetDescriptor.FileCount; else fileTableItems = cabinetDescriptor.DirectoryCount; // Create and fill the file table - header.FileDescriptorOffsets = new uint[fileTableItems]; - for (int i = 0; i < header.FileDescriptorOffsets.Length; i++) + cabinet.FileDescriptorOffsets = new uint[fileTableItems]; + for (int i = 0; i < cabinet.FileDescriptorOffsets.Length; i++) { - header.FileDescriptorOffsets[i] = data.ReadUInt32(); + cabinet.FileDescriptorOffsets[i] = data.ReadUInt32(); } #endregion @@ -119,13 +131,13 @@ namespace BurnOutSharp.Builders #region Directory Descriptors // Create and fill the directory descriptors - header.DirectoryDescriptors = new FileDescriptor[cabinetDescriptor.DirectoryCount]; + cabinet.DirectoryDescriptors = new FileDescriptor[cabinetDescriptor.DirectoryCount]; for (int i = 0; i < cabinetDescriptor.DirectoryCount; i++) { // Get the directory descriptor offset uint offset = cabinetDescriptorOffset + cabinetDescriptor.FileTableOffset - + header.FileDescriptorOffsets[i]; + + cabinet.FileDescriptorOffsets[i]; // If we have an invalid offset if (offset < 0 || offset >= data.Length) @@ -135,8 +147,8 @@ namespace BurnOutSharp.Builders data.Seek(offset, SeekOrigin.Begin); // Create and add the file descriptor - FileDescriptor directoryDescriptor = ParseDirectoryDescriptor(data, header.MajorVersion); - header.DirectoryDescriptors[i] = directoryDescriptor; + FileDescriptor directoryDescriptor = ParseDirectoryDescriptor(data, GetMajorVersion(commonHeader)); + cabinet.DirectoryDescriptors[i] = directoryDescriptor; } #endregion @@ -144,16 +156,16 @@ namespace BurnOutSharp.Builders #region File Descriptors // Create and fill the file descriptors - header.FileDescriptors = new FileDescriptor[cabinetDescriptor.FileCount]; + cabinet.FileDescriptors = new FileDescriptor[cabinetDescriptor.FileCount]; for (int i = 0; i < cabinetDescriptor.FileCount; i++) { // Get the file descriptor offset uint offset; - if (header.MajorVersion <= 5) + if (GetMajorVersion(commonHeader) <= 5) { offset = cabinetDescriptorOffset + cabinetDescriptor.FileTableOffset - + header.FileDescriptorOffsets[cabinetDescriptor.DirectoryCount + i]; + + cabinet.FileDescriptorOffsets[cabinetDescriptor.DirectoryCount + i]; } else { @@ -171,8 +183,8 @@ namespace BurnOutSharp.Builders data.Seek(offset, SeekOrigin.Begin); // Create and add the file descriptor - FileDescriptor fileDescriptor = ParseFileDescriptor(data, header.MajorVersion, cabinetDescriptorOffset + cabinetDescriptor.FileTableOffset); - header.FileDescriptors[i] = fileDescriptor; + FileDescriptor fileDescriptor = ParseFileDescriptor(data, GetMajorVersion(commonHeader), cabinetDescriptorOffset + cabinetDescriptor.FileTableOffset); + cabinet.FileDescriptors[i] = fileDescriptor; } #endregion @@ -180,7 +192,7 @@ namespace BurnOutSharp.Builders #region File Group Offsets // Create and fill the file group offsets - header.FileGroupOffsets = new Dictionary(); + cabinet.FileGroupOffsets = new Dictionary(); for (int i = 0; i < cabinetDescriptor.FileGroupOffsets.Length; i++) { // Get the file group offset @@ -197,8 +209,8 @@ namespace BurnOutSharp.Builders data.Seek(offset, SeekOrigin.Begin); // Create and add the offset - OffsetList offsetList = ParseOffsetList(data, header.MajorVersion, cabinetDescriptorOffset); - header.FileGroupOffsets[cabinetDescriptor.FileGroupOffsets[i]] = offsetList; + OffsetList offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), cabinetDescriptorOffset); + cabinet.FileGroupOffsets[cabinetDescriptor.FileGroupOffsets[i]] = offsetList; // If we have a nonzero next offset uint nextOffset = offsetList.NextOffset; @@ -211,8 +223,8 @@ namespace BurnOutSharp.Builders data.Seek(internalOffset, SeekOrigin.Begin); // Create and add the offset - offsetList = ParseOffsetList(data, header.MajorVersion, cabinetDescriptorOffset); - header.FileGroupOffsets[nextOffset] = offsetList; + offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), cabinetDescriptorOffset); + cabinet.FileGroupOffsets[nextOffset] = offsetList; // Set the next offset nextOffset = offsetList.NextOffset; @@ -225,7 +237,7 @@ namespace BurnOutSharp.Builders // Create and fill the file groups List fileGroups = new List(); - foreach (var kvp in header.FileGroupOffsets) + foreach (var kvp in cabinet.FileGroupOffsets) { // Get the offset OffsetList list = kvp.Value; @@ -236,21 +248,21 @@ namespace BurnOutSharp.Builders data.Seek(list.DescriptorOffset + cabinetDescriptorOffset, SeekOrigin.Begin); // Try to parse the file group - FileGroup fileGroup = ParseFileGroup(data, header.MajorVersion, cabinetDescriptorOffset); + FileGroup fileGroup = ParseFileGroup(data, GetMajorVersion(commonHeader), cabinetDescriptorOffset); // Add the file group fileGroups.Add(fileGroup); } // Set the file groups - header.FileGroups = fileGroups.ToArray(); + cabinet.FileGroups = fileGroups.ToArray(); #endregion #region Component Offsets // Create and fill the component offsets - header.ComponentOffsets = new Dictionary(); + cabinet.ComponentOffsets = new Dictionary(); for (int i = 0; i < cabinetDescriptor.ComponentOffsets.Length; i++) { // Get the component offset @@ -267,8 +279,8 @@ namespace BurnOutSharp.Builders data.Seek(offset, SeekOrigin.Begin); // Create and add the offset - OffsetList offsetList = ParseOffsetList(data, header.MajorVersion, cabinetDescriptorOffset); - header.ComponentOffsets[cabinetDescriptor.ComponentOffsets[i]] = offsetList; + OffsetList offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), cabinetDescriptorOffset); + cabinet.ComponentOffsets[cabinetDescriptor.ComponentOffsets[i]] = offsetList; // If we have a nonzero next offset uint nextOffset = offsetList.NextOffset; @@ -281,8 +293,8 @@ namespace BurnOutSharp.Builders data.Seek(internalOffset, SeekOrigin.Begin); // Create and add the offset - offsetList = ParseOffsetList(data, header.MajorVersion, cabinetDescriptorOffset); - header.ComponentOffsets[nextOffset] = offsetList; + offsetList = ParseOffsetList(data, GetMajorVersion(commonHeader), cabinetDescriptorOffset); + cabinet.ComponentOffsets[nextOffset] = offsetList; // Set the next offset nextOffset = offsetList.NextOffset; @@ -295,7 +307,7 @@ namespace BurnOutSharp.Builders // Create and fill the components List components = new List(); - foreach (KeyValuePair kvp in header.ComponentOffsets) + foreach (KeyValuePair kvp in cabinet.ComponentOffsets) { // Get the offset OffsetList list = kvp.Value; @@ -306,18 +318,18 @@ namespace BurnOutSharp.Builders data.Seek(list.DescriptorOffset + cabinetDescriptorOffset, SeekOrigin.Begin); // Try to parse the component - Component component = ParseComponent(data, header.MajorVersion, cabinetDescriptorOffset); + Component component = ParseComponent(data, GetMajorVersion(commonHeader), cabinetDescriptorOffset); // Add the component components.Add(component); } // Set the components - header.Components = components.ToArray(); + cabinet.Components = components.ToArray(); #endregion - return header; + return cabinet; } /// @@ -342,6 +354,53 @@ namespace BurnOutSharp.Builders return commonHeader; } + /// + /// Parse a Stream into a volume header + /// + /// Stream to parse + /// Major version of the cabinet + /// Filled volume header on success, null on error + private static VolumeHeader ParseVolumeHeader(Stream data, int majorVersion) + { + VolumeHeader volumeHeader = new VolumeHeader(); + + // Read the descriptor based on version + if (majorVersion <= 5) + { + volumeHeader.DataOffset = data.ReadUInt32(); + _ = data.ReadBytes(0x04); // Skip 0x04 bytes, unknown data? + volumeHeader.FirstFileIndex = data.ReadUInt32(); + volumeHeader.LastFileIndex = data.ReadUInt32(); + volumeHeader.FirstFileOffset = data.ReadUInt32(); + volumeHeader.FirstFileSizeExpanded = data.ReadUInt32(); + volumeHeader.FirstFileSizeCompressed = data.ReadUInt32(); + volumeHeader.LastFileOffset = data.ReadUInt32(); + volumeHeader.LastFileSizeExpanded = data.ReadUInt32(); + volumeHeader.LastFileSizeCompressed = data.ReadUInt32(); + } + else + { + volumeHeader.DataOffset = data.ReadUInt32(); + volumeHeader.DataOffsetHigh = data.ReadUInt32(); + volumeHeader.FirstFileIndex = data.ReadUInt32(); + volumeHeader.LastFileIndex = data.ReadUInt32(); + volumeHeader.FirstFileOffset = data.ReadUInt32(); + volumeHeader.FirstFileOffsetHigh = data.ReadUInt32(); + volumeHeader.FirstFileSizeExpanded = data.ReadUInt32(); + volumeHeader.FirstFileSizeExpandedHigh = data.ReadUInt32(); + volumeHeader.FirstFileSizeCompressed = data.ReadUInt32(); + volumeHeader.FirstFileSizeCompressedHigh = data.ReadUInt32(); + volumeHeader.LastFileOffset = data.ReadUInt32(); + volumeHeader.LastFileOffsetHigh = data.ReadUInt32(); + volumeHeader.LastFileSizeExpanded = data.ReadUInt32(); + volumeHeader.LastFileSizeExpandedHigh = data.ReadUInt32(); + volumeHeader.LastFileSizeCompressed = data.ReadUInt32(); + volumeHeader.LastFileSizeCompressedHigh = data.ReadUInt32(); + } + + return volumeHeader; + } + /// /// Parse a Stream into a cabinet descriptor /// @@ -599,51 +658,29 @@ namespace BurnOutSharp.Builders return fileDescriptor; } + #endregion + + #region Helpers + /// - /// Parse a Stream into a volume header + /// Get the major version of the cabinet /// - /// Stream to parse - /// Major version of the cabinet - /// Filled volume header on success, null on error - private static VolumeHeader ParseVolumeHeader(Stream data, int majorVersion) + /// This should live in the wrapper but is needed during parsing + private static int GetMajorVersion(CommonHeader commonHeader) { - VolumeHeader volumeHeader = new VolumeHeader(); - - // Read the descriptor based on version - if (majorVersion <= 5) + uint majorVersion = commonHeader.Version; + if (majorVersion >> 24 == 1) { - volumeHeader.DataOffset = data.ReadUInt32(); - _ = data.ReadBytes(0x04); // Skip 0x04 bytes, unknown data? - volumeHeader.FirstFileIndex = data.ReadUInt32(); - volumeHeader.LastFileIndex = data.ReadUInt32(); - volumeHeader.FirstFileOffset = data.ReadUInt32(); - volumeHeader.FirstFileSizeExpanded = data.ReadUInt32(); - volumeHeader.FirstFileSizeCompressed = data.ReadUInt32(); - volumeHeader.LastFileOffset = data.ReadUInt32(); - volumeHeader.LastFileSizeExpanded = data.ReadUInt32(); - volumeHeader.LastFileSizeCompressed = data.ReadUInt32(); + majorVersion = (majorVersion >> 12) & 0x0F; } - else + else if (majorVersion >> 24 == 2 || majorVersion >> 24 == 4) { - volumeHeader.DataOffset = data.ReadUInt32(); - volumeHeader.DataOffsetHigh = data.ReadUInt32(); - volumeHeader.FirstFileIndex = data.ReadUInt32(); - volumeHeader.LastFileIndex = data.ReadUInt32(); - volumeHeader.FirstFileOffset = data.ReadUInt32(); - volumeHeader.FirstFileOffsetHigh = data.ReadUInt32(); - volumeHeader.FirstFileSizeExpanded = data.ReadUInt32(); - volumeHeader.FirstFileSizeExpandedHigh = data.ReadUInt32(); - volumeHeader.FirstFileSizeCompressed = data.ReadUInt32(); - volumeHeader.FirstFileSizeCompressedHigh = data.ReadUInt32(); - volumeHeader.LastFileOffset = data.ReadUInt32(); - volumeHeader.LastFileOffsetHigh = data.ReadUInt32(); - volumeHeader.LastFileSizeExpanded = data.ReadUInt32(); - volumeHeader.LastFileSizeExpandedHigh = data.ReadUInt32(); - volumeHeader.LastFileSizeCompressed = data.ReadUInt32(); - volumeHeader.LastFileSizeCompressedHigh = data.ReadUInt32(); + majorVersion = majorVersion & 0xFFFF; + if (majorVersion != 0) + majorVersion /= 100; } - return volumeHeader; + return (int)majorVersion; } #endregion diff --git a/BurnOutSharp.Models/InstallShieldCabinet/Cabinet.cs b/BurnOutSharp.Models/InstallShieldCabinet/Cabinet.cs new file mode 100644 index 00000000..266b29b1 --- /dev/null +++ b/BurnOutSharp.Models/InstallShieldCabinet/Cabinet.cs @@ -0,0 +1,75 @@ +using System.Collections.Generic; + +namespace BurnOutSharp.Models.InstallShieldCabinet +{ + /// + /// + public sealed class Cabinet + { + #region Headers + + /// + /// Common header + /// + public CommonHeader CommonHeader { get; set; } + + /// + /// Volume header + /// + public VolumeHeader VolumeHeader { get; set; } + + /// + /// Cabinet descriptor + /// + public CabDescriptor CabinetDescriptor { get; set; } + + #endregion + + #region File Descriptors + + /// + /// Offsets to all file descriptors + /// + public uint[] FileDescriptorOffsets { get; set; } + + /// + /// Directory file descriptors + /// + public FileDescriptor[] DirectoryDescriptors { get; set; } + + /// + /// Standard file descriptors + /// + public FileDescriptor[] FileDescriptors { get; set; } + + #endregion + + #region File Groups + + /// + /// File group offset to offset list mapping + /// + public Dictionary FileGroupOffsets { get; set; } + + /// + /// File groups + /// + public FileGroup[] FileGroups { get; set; } + + #endregion + + #region Components + + /// + /// Component offset to offset list mapping + /// + public Dictionary ComponentOffsets { get; set; } + + /// + /// Components + /// + public Component[] Components { get; set; } + + #endregion + } +} \ No newline at end of file diff --git a/BurnOutSharp.Models/InstallShieldCabinet/Header.cs b/BurnOutSharp.Models/InstallShieldCabinet/Header.cs deleted file mode 100644 index 8c0d690a..00000000 --- a/BurnOutSharp.Models/InstallShieldCabinet/Header.cs +++ /dev/null @@ -1,63 +0,0 @@ -using System.Collections.Generic; - -namespace BurnOutSharp.Models.InstallShieldCabinet -{ - /// - public sealed class Header - { - // TODO: Move to wrapper, when exists - public int MajorVersion - { - get - { - uint majorVersion = CommonHeader.Version; - if (majorVersion >> 24 == 1) - { - majorVersion = (majorVersion >> 12) & 0x0F; - } - else if (majorVersion >> 24 == 2 || majorVersion >> 24 == 4) - { - majorVersion = majorVersion & 0xFFFF; - if (majorVersion != 0) - majorVersion /= 100; - } - - return (int)majorVersion; - } - } - - #region Headers - - public CommonHeader CommonHeader { get; set; } - - public CabDescriptor CabinetDescriptor { get; set; } - - #endregion - - #region File Descriptors - - public uint[] FileDescriptorOffsets { get; set; } - - public FileDescriptor[] DirectoryDescriptors { get; set; } - - public FileDescriptor[] FileDescriptors { get; set; } - - #endregion - - #region File Groups - - public Dictionary FileGroupOffsets { get; set; } - - public FileGroup[] FileGroups { get; set; } - - #endregion - - #region Components - - public Dictionary ComponentOffsets { get; set; } - - public Component[] Components { get; set; } - - #endregion - } -} \ No newline at end of file