diff --git a/BurnOutSharp.Builders/InstallShieldCabinet.cs b/BurnOutSharp.Builders/InstallShieldCabinet.cs
index a7c84fff..e8d3a780 100644
--- a/BurnOutSharp.Builders/InstallShieldCabinet.cs
+++ b/BurnOutSharp.Builders/InstallShieldCabinet.cs
@@ -380,6 +380,7 @@ namespace BurnOutSharp.Builders
}
else
{
+ // TODO: Should standard and high values be combined?
volumeHeader.DataOffset = data.ReadUInt32();
volumeHeader.DataOffsetHigh = data.ReadUInt32();
volumeHeader.FirstFileIndex = data.ReadUInt32();
diff --git a/BurnOutSharp.Models/InstallShieldCabinet/VolumeHeader.cs b/BurnOutSharp.Models/InstallShieldCabinet/VolumeHeader.cs
index b3877fb6..f60f1ed9 100644
--- a/BurnOutSharp.Models/InstallShieldCabinet/VolumeHeader.cs
+++ b/BurnOutSharp.Models/InstallShieldCabinet/VolumeHeader.cs
@@ -1,6 +1,7 @@
namespace BurnOutSharp.Models.InstallShieldCabinet
{
///
+ /// TODO: Should standard and high values be combined?
public sealed class VolumeHeader
{
public uint DataOffset;
diff --git a/BurnOutSharp.Wrappers/InstallShieldCabinet.cs b/BurnOutSharp.Wrappers/InstallShieldCabinet.cs
new file mode 100644
index 00000000..8c43241e
--- /dev/null
+++ b/BurnOutSharp.Wrappers/InstallShieldCabinet.cs
@@ -0,0 +1,538 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+
+namespace BurnOutSharp.Wrappers
+{
+ public partial class InstallShieldCabinet : WrapperBase
+ {
+ #region Pass-Through Properties
+
+ #region Common Header
+
+ ///
+ public string Signature => _cabinet.CommonHeader.Signature;
+
+ ///
+ public uint Version => _cabinet.CommonHeader.Version;
+
+ ///
+ public uint VolumeInfo => _cabinet.CommonHeader.VolumeInfo;
+
+ ///
+ public uint CabDescriptorOffset => _cabinet.CommonHeader.CabDescriptorOffset;
+
+ ///
+ public uint CabDescriptorSize => _cabinet.CommonHeader.CabDescriptorSize;
+
+ #endregion
+
+ #region Volume Header
+
+ ///
+ public uint DataOffset => _cabinet.VolumeHeader.DataOffset;
+
+ ///
+ public uint DataOffsetHigh => _cabinet.VolumeHeader.DataOffsetHigh;
+
+ ///
+ public uint FirstFileIndex => _cabinet.VolumeHeader.FirstFileIndex;
+
+ ///
+ public uint LastFileIndex => _cabinet.VolumeHeader.LastFileIndex;
+
+ ///
+ public uint FirstFileOffset => _cabinet.VolumeHeader.FirstFileOffset;
+
+ ///
+ public uint FirstFileOffsetHigh => _cabinet.VolumeHeader.FirstFileOffsetHigh;
+
+ ///
+ public uint FirstFileSizeExpanded => _cabinet.VolumeHeader.FirstFileSizeExpanded;
+
+ ///
+ public uint FirstFileSizeExpandedHigh => _cabinet.VolumeHeader.FirstFileSizeExpandedHigh;
+
+ ///
+ public uint FirstFileSizeCompressed => _cabinet.VolumeHeader.FirstFileSizeCompressed;
+
+ ///
+ public uint FirstFileSizeCompressedHigh => _cabinet.VolumeHeader.FirstFileSizeCompressedHigh;
+
+ ///
+ public uint LastFileOffset => _cabinet.VolumeHeader.LastFileOffset;
+
+ ///
+ public uint LastFileOffsetHigh => _cabinet.VolumeHeader.LastFileOffsetHigh;
+
+ ///
+ public uint LastFileSizeExpanded => _cabinet.VolumeHeader.LastFileSizeExpanded;
+
+ ///
+ public uint LastFileSizeExpandedHigh => _cabinet.VolumeHeader.LastFileSizeExpandedHigh;
+
+ ///
+ public uint LastFileSizeCompressed => _cabinet.VolumeHeader.LastFileSizeCompressed;
+
+ ///
+ public uint LastFileSizeCompressedHigh => _cabinet.VolumeHeader.LastFileSizeCompressedHigh;
+
+ #endregion
+
+ #region File Descriptor Offsets
+
+ ///
+ public uint[] FileDescriptorOffsets => _cabinet.FileDescriptorOffsets;
+
+ #endregion
+
+ #region Directory Descriptors
+
+ ///
+ public Models.InstallShieldCabinet.FileDescriptor[] DirectoryDescriptors => _cabinet.DirectoryDescriptors;
+
+ #endregion
+
+ #region File Descriptors
+
+ ///
+ public Models.InstallShieldCabinet.FileDescriptor[] FileDescriptors => _cabinet.FileDescriptors;
+
+ #endregion
+
+ #region File Group Offsets
+
+ ///
+ public Dictionary FileGroupOffsets => _cabinet.FileGroupOffsets;
+
+ #endregion
+
+ #region File Groups
+
+ ///
+ public Models.InstallShieldCabinet.FileGroup[] FileGroups => _cabinet.FileGroups;
+
+ #endregion
+
+ #region Component Offsets
+
+ ///
+ public Dictionary ComponentOffsets => _cabinet.ComponentOffsets;
+
+ #endregion
+
+ #region Components
+
+ ///
+ public Models.InstallShieldCabinet.Component[] Components => _cabinet.Components;
+
+ #endregion
+
+ #endregion
+
+ #region Extension Properties
+
+ ///
+ /// The major version of the cabinet
+ ///
+ public int MajorVersion
+ {
+ get
+ {
+ uint majorVersion = 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;
+ }
+ }
+
+ #endregion
+
+ #region Instance Variables
+
+ ///
+ /// Internal representation of the cabinet
+ ///
+ private Models.InstallShieldCabinet.Cabinet _cabinet;
+
+ #endregion
+
+ #region Constructors
+
+ ///
+ /// Private constructor
+ ///
+ private InstallShieldCabinet() { }
+
+ ///
+ /// Create an InstallShield Cabinet from a byte array and offset
+ ///
+ /// Byte array representing the cabinet
+ /// Offset within the array to parse
+ /// A cabinet wrapper on success, null on failure
+ public static InstallShieldCabinet Create(byte[] data, int offset)
+ {
+ // If the data is invalid
+ if (data == null)
+ return null;
+
+ // If the offset is out of bounds
+ if (offset < 0 || offset >= data.Length)
+ return null;
+
+ // Create a memory stream and use that
+ MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
+ return Create(dataStream);
+ }
+
+ ///
+ /// Create a InstallShield Cabinet from a Stream
+ ///
+ /// Stream representing the cabinet
+ /// A cabinet wrapper on success, null on failure
+ public static InstallShieldCabinet Create(Stream data)
+ {
+ // If the data is invalid
+ if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
+ return null;
+
+ var cabinet = Builders.InstallShieldCabinet.ParseCabinet(data);
+ if (cabinet == null)
+ return null;
+
+ var wrapper = new InstallShieldCabinet
+ {
+ _cabinet = cabinet,
+ _dataSource = DataSource.Stream,
+ _streamData = data,
+ };
+ return wrapper;
+ }
+
+ #endregion
+
+ #region Printing
+
+ ///
+ public override StringBuilder PrettyPrint()
+ {
+ StringBuilder builder = new StringBuilder();
+
+ builder.AppendLine("InstallShield Cabinet Information:");
+ builder.AppendLine("-------------------------");
+ builder.AppendLine();
+
+ // Headers
+ PrintCommonHeader(builder);
+ PrintVolumeHeader(builder);
+
+ // File Descriptors
+ PrintFileDescriptorOffsets(builder);
+ PrintDirectoryDescriptors(builder);
+ PrintFileDescriptors(builder);
+
+ // File Groups
+ PrintFileGroupOffsets(builder);
+ PrintFileGroups(builder);
+
+ // Components
+ PrintComponentOffsets(builder);
+ PrintComponents(builder);
+
+ return builder;
+ }
+
+ ///
+ /// Print common header information
+ ///
+ /// StringBuilder to append information to
+ private void PrintCommonHeader(StringBuilder builder)
+ {
+ builder.AppendLine(" Common Header Information:");
+ builder.AppendLine(" -------------------------");
+ builder.AppendLine($" Signature: {Signature}");
+ builder.AppendLine($" Version: {Version} (0x{Version:X}) [{MajorVersion}]");
+ builder.AppendLine($" Volume info: {VolumeInfo} (0x{VolumeInfo:X})");
+ builder.AppendLine($" Cabinet descriptor offset: {CabDescriptorOffset} (0x{CabDescriptorOffset:X})");
+ builder.AppendLine($" Cabinet descriptor size: {CabDescriptorSize} (0x{CabDescriptorSize:X})");
+ builder.AppendLine();
+ }
+
+ ///
+ /// Print volume header information
+ ///
+ /// StringBuilder to append information to
+ private void PrintVolumeHeader(StringBuilder builder)
+ {
+ builder.AppendLine(" Volume Header Information:");
+ builder.AppendLine(" -------------------------");
+ if (MajorVersion <= 5)
+ {
+ builder.AppendLine($" Data offset: {DataOffset} (0x{DataOffset:X})");
+ builder.AppendLine($" First file index: {FirstFileIndex} (0x{FirstFileIndex:X})");
+ builder.AppendLine($" Last file index: {LastFileIndex} (0x{LastFileIndex:X})");
+ builder.AppendLine($" First file offset: {FirstFileOffset} (0x{FirstFileOffset:X})");
+ builder.AppendLine($" First file size expanded: {FirstFileSizeExpanded} (0x{FirstFileSizeExpanded:X})");
+ builder.AppendLine($" First file size compressed: {FirstFileSizeCompressed} (0x{FirstFileSizeCompressed:X})");
+ builder.AppendLine($" Last file offset: {LastFileOffset} (0x{LastFileOffset:X})");
+ builder.AppendLine($" Last file size expanded: {LastFileSizeExpanded} (0x{LastFileSizeExpanded:X})");
+ builder.AppendLine($" Last file size compressed: {LastFileSizeCompressed} (0x{LastFileSizeCompressed:X})");
+ }
+ else
+ {
+ // TODO: Should standard and high values be combined?
+ builder.AppendLine($" Data offset: {DataOffset} (0x{DataOffset:X})");
+ builder.AppendLine($" Data offset high: {DataOffsetHigh} (0x{DataOffsetHigh:X})");
+ builder.AppendLine($" First file index: {FirstFileIndex} (0x{FirstFileIndex:X})");
+ builder.AppendLine($" Last file index: {LastFileIndex} (0x{LastFileIndex:X})");
+ builder.AppendLine($" First file offset: {FirstFileOffset} (0x{FirstFileOffset:X})");
+ builder.AppendLine($" First file offset high: {FirstFileOffsetHigh} (0x{FirstFileOffsetHigh:X})");
+ builder.AppendLine($" First file size expanded: {FirstFileSizeExpanded} (0x{FirstFileSizeExpanded:X})");
+ builder.AppendLine($" First file size expanded high: {FirstFileSizeExpandedHigh} (0x{FirstFileSizeExpandedHigh:X})");
+ builder.AppendLine($" First file size compressed: {FirstFileSizeCompressed} (0x{FirstFileSizeCompressed:X})");
+ builder.AppendLine($" First file size compressed high: {FirstFileSizeCompressedHigh} (0x{FirstFileSizeCompressedHigh:X})");
+ builder.AppendLine($" Last file offset: {LastFileOffset} (0x{LastFileOffset:X})");
+ builder.AppendLine($" Last file offset high: {LastFileOffsetHigh} (0x{LastFileOffsetHigh:X})");
+ builder.AppendLine($" Last file size expanded: {LastFileSizeExpanded} (0x{LastFileSizeExpanded:X})");
+ builder.AppendLine($" Last file size expanded high: {LastFileSizeExpandedHigh} (0x{LastFileSizeExpandedHigh:X})");
+ builder.AppendLine($" Last file size compressed: {LastFileSizeCompressed} (0x{LastFileSizeCompressed:X})");
+ builder.AppendLine($" Last file size compressed high: {LastFileSizeCompressedHigh} (0x{LastFileSizeCompressedHigh:X})");
+ }
+ builder.AppendLine();
+ }
+
+ ///
+ /// Print file descriptor offsets information
+ ///
+ /// StringBuilder to append information to
+ private void PrintFileDescriptorOffsets(StringBuilder builder)
+ {
+ builder.AppendLine(" File Descriptor Offsets:");
+ builder.AppendLine(" -------------------------");
+ if (FileDescriptorOffsets == null || FileDescriptorOffsets.Length == 0)
+ {
+ builder.AppendLine(" No file descriptor offsets");
+ }
+ else
+ {
+ for (int i = 0; i < FileDescriptorOffsets.Length; i++)
+ {
+ builder.AppendLine($" File Descriptor Offset {i}: {FileDescriptorOffsets[i]} (0x{FileDescriptorOffsets[i]:X})");
+ }
+ }
+ builder.AppendLine();
+ }
+
+ ///
+ /// Print directory descriptors information
+ ///
+ /// StringBuilder to append information to
+ private void PrintDirectoryDescriptors(StringBuilder builder)
+ {
+ builder.AppendLine(" Directory Descriptors:");
+ builder.AppendLine(" -------------------------");
+ if (DirectoryDescriptors == null || DirectoryDescriptors.Length == 0)
+ {
+ builder.AppendLine(" No directory descriptors");
+ }
+ else
+ {
+ for (int i = 0; i < DirectoryDescriptors.Length; i++)
+ {
+ var directoryDescriptor = DirectoryDescriptors[i];
+ builder.AppendLine($" Directory Descriptor {i}:");
+ builder.AppendLine($" Name offset: {directoryDescriptor.NameOffset} (0x{directoryDescriptor.NameOffset:X})");
+ builder.AppendLine($" Name: {directoryDescriptor.Name ?? "[NULL]"}");
+ builder.AppendLine($" Directory index: {directoryDescriptor.DirectoryIndex} (0x{directoryDescriptor.DirectoryIndex:X})");
+ builder.AppendLine($" Flags: {directoryDescriptor.Flags} (0x{directoryDescriptor.Flags:X})");
+ builder.AppendLine($" Expanded size: {directoryDescriptor.ExpandedSize} (0x{directoryDescriptor.ExpandedSize:X})");
+ builder.AppendLine($" Compressed size: {directoryDescriptor.CompressedSize} (0x{directoryDescriptor.CompressedSize:X})");
+ builder.AppendLine($" Data offset: {directoryDescriptor.DataOffset} (0x{directoryDescriptor.DataOffset:X})");
+ builder.AppendLine($" MD5: {BitConverter.ToString(directoryDescriptor.MD5 ?? new byte[0]).Replace('-', ' ')}");
+ builder.AppendLine($" Volume: {directoryDescriptor.Volume} (0x{directoryDescriptor.Volume:X})");
+ builder.AppendLine($" Link previous: {directoryDescriptor.LinkPrevious} (0x{directoryDescriptor.LinkPrevious:X})");
+ builder.AppendLine($" Link next: {directoryDescriptor.LinkNext} (0x{directoryDescriptor.LinkNext:X})");
+ builder.AppendLine($" Link flags: {directoryDescriptor.LinkFlags} (0x{directoryDescriptor.LinkFlags:X})");
+ }
+ }
+ builder.AppendLine();
+ }
+
+ ///
+ /// Print file descriptors information
+ ///
+ /// StringBuilder to append information to
+ private void PrintFileDescriptors(StringBuilder builder)
+ {
+ builder.AppendLine(" File Descriptors:");
+ builder.AppendLine(" -------------------------");
+ if (FileDescriptors == null || FileDescriptors.Length == 0)
+ {
+ builder.AppendLine(" No file descriptors");
+ }
+ else
+ {
+ for (int i = 0; i < FileDescriptors.Length; i++)
+ {
+ var fileDescriptor = FileDescriptors[i];
+ builder.AppendLine($" File Descriptor {i}:");
+ builder.AppendLine($" Name offset: {fileDescriptor.NameOffset} (0x{fileDescriptor.NameOffset:X})");
+ builder.AppendLine($" Name: {fileDescriptor.Name ?? "[NULL]"}");
+ builder.AppendLine($" Directory index: {fileDescriptor.DirectoryIndex} (0x{fileDescriptor.DirectoryIndex:X})");
+ builder.AppendLine($" Flags: {fileDescriptor.Flags} (0x{fileDescriptor.Flags:X})");
+ builder.AppendLine($" Expanded size: {fileDescriptor.ExpandedSize} (0x{fileDescriptor.ExpandedSize:X})");
+ builder.AppendLine($" Compressed size: {fileDescriptor.CompressedSize} (0x{fileDescriptor.CompressedSize:X})");
+ builder.AppendLine($" Data offset: {fileDescriptor.DataOffset} (0x{fileDescriptor.DataOffset:X})");
+ builder.AppendLine($" MD5: {BitConverter.ToString(fileDescriptor.MD5 ?? new byte[0]).Replace('-', ' ')}");
+ builder.AppendLine($" Volume: {fileDescriptor.Volume} (0x{fileDescriptor.Volume:X})");
+ builder.AppendLine($" Link previous: {fileDescriptor.LinkPrevious} (0x{fileDescriptor.LinkPrevious:X})");
+ builder.AppendLine($" Link next: {fileDescriptor.LinkNext} (0x{fileDescriptor.LinkNext:X})");
+ builder.AppendLine($" Link flags: {fileDescriptor.LinkFlags} (0x{fileDescriptor.LinkFlags:X})");
+ }
+ }
+ builder.AppendLine();
+ }
+
+ ///
+ /// Print file group offsets information
+ ///
+ /// StringBuilder to append information to
+ private void PrintFileGroupOffsets(StringBuilder builder)
+ {
+ builder.AppendLine(" File Group Offsets:");
+ builder.AppendLine(" -------------------------");
+ if (FileGroupOffsets == null || FileGroupOffsets.Count == 0)
+ {
+ builder.AppendLine(" No file group offsets");
+ }
+ else
+ {
+ foreach (var kvp in FileGroupOffsets)
+ {
+ long offset = kvp.Key;
+ var offsetList = kvp.Value;
+ builder.AppendLine($" File Group Offset {offset}:");
+ builder.AppendLine($" Name offset: {offsetList.NameOffset} (0x{offsetList.NameOffset:X})");
+ builder.AppendLine($" Name: {offsetList.Name ?? "[NULL]"}");
+ builder.AppendLine($" Descriptor offset: {offsetList.DescriptorOffset} (0x{offsetList.DescriptorOffset:X})");
+ builder.AppendLine($" Next offset: {offsetList.NextOffset} (0x{offsetList.NextOffset:X})");
+ }
+ }
+ builder.AppendLine();
+ }
+
+ ///
+ /// Print file groups information
+ ///
+ /// StringBuilder to append information to
+ private void PrintFileGroups(StringBuilder builder)
+ {
+ builder.AppendLine(" File Groups:");
+ builder.AppendLine(" -------------------------");
+ if (FileGroups == null || FileGroups.Length == 0)
+ {
+ builder.AppendLine(" No file groups");
+ }
+ else
+ {
+ for (int i = 0; i < FileGroups.Length; i++)
+ {
+ var fileGroup = FileGroups[i];
+ builder.AppendLine($" File Group {i}:");
+ builder.AppendLine($" Name offset: {fileGroup.NameOffset} (0x{fileGroup.NameOffset:X})");
+ builder.AppendLine($" Name: {fileGroup.Name ?? "[NULL]"}");
+ builder.AppendLine($" First file: {fileGroup.FirstFile} (0x{fileGroup.FirstFile:X})");
+ builder.AppendLine($" Last file: {fileGroup.LastFile} (0x{fileGroup.LastFile:X})");
+ }
+ }
+ builder.AppendLine();
+ }
+
+ ///
+ /// Print component offsets information
+ ///
+ /// StringBuilder to append information to
+ private void PrintComponentOffsets(StringBuilder builder)
+ {
+ builder.AppendLine(" Component Offsets:");
+ builder.AppendLine(" -------------------------");
+ if (ComponentOffsets == null || ComponentOffsets.Count == 0)
+ {
+ builder.AppendLine(" No component offsets");
+ }
+ else
+ {
+ foreach (var kvp in ComponentOffsets)
+ {
+ long offset = kvp.Key;
+ var offsetList = kvp.Value;
+ builder.AppendLine($" Component Offset {offset}:");
+ builder.AppendLine($" Name offset: {offsetList.NameOffset} (0x{offsetList.NameOffset:X})");
+ builder.AppendLine($" Name: {offsetList.Name ?? "[NULL]"}");
+ builder.AppendLine($" Descriptor offset: {offsetList.DescriptorOffset} (0x{offsetList.DescriptorOffset:X})");
+ builder.AppendLine($" Next offset: {offsetList.NextOffset} (0x{offsetList.NextOffset:X})");
+ }
+ }
+ builder.AppendLine();
+ }
+
+ ///
+ /// Print components information
+ ///
+ /// StringBuilder to append information to
+ private void PrintComponents(StringBuilder builder)
+ {
+ builder.AppendLine(" Components:");
+ builder.AppendLine(" -------------------------");
+ if (Components == null || Components.Length == 0)
+ {
+ builder.AppendLine(" No components");
+ }
+ else
+ {
+ for (int i = 0; i < Components.Length; i++)
+ {
+ var component = Components[i];
+ builder.AppendLine($" Component {i}:");
+ builder.AppendLine($" Name offset: {component.NameOffset} (0x{component.NameOffset:X})");
+ builder.AppendLine($" Name: {component.Name ?? "[NULL]"}");
+ builder.AppendLine($" File group count: {component.FileGroupCount} (0x{component.FileGroupCount:X})");
+ builder.AppendLine($" File group table offset: {component.FileGroupTableOffset} (0x{component.FileGroupTableOffset:X})");
+ builder.AppendLine($" File group names:");
+ builder.AppendLine(" -------------------------");
+ if (component.FileGroupNames == null || component.FileGroupNames.Length == 0)
+ {
+ builder.AppendLine(" No file group names");
+ }
+ else
+ {
+ for (int j = 0; j < component.FileGroupNames.Length; j++)
+ {
+ builder.AppendLine($" File Group Name {j}: {component.FileGroupNames[j] ?? "[NULL]"}");
+ }
+ }
+ }
+ }
+ builder.AppendLine();
+ }
+
+#if NET6_0_OR_GREATER
+
+ ///
+ public override string ExportJSON() => System.Text.Json.JsonSerializer.Serialize(_cabinet, _jsonSerializerOptions);
+
+#endif
+
+ #endregion
+ }
+}