diff --git a/BurnOutSharp.Builders/InstallShieldCabinet.cs b/BurnOutSharp.Builders/InstallShieldCabinet.cs
new file mode 100644
index 00000000..459febe2
--- /dev/null
+++ b/BurnOutSharp.Builders/InstallShieldCabinet.cs
@@ -0,0 +1,651 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Text;
+using BurnOutSharp.Models.InstallShieldCabinet;
+using BurnOutSharp.Utilities;
+
+namespace BurnOutSharp.Builders
+{
+ // TODO: Add multi-cabinet reading
+ public class InstallShieldCabinet
+ {
+ #region Byte Data
+
+ ///
+ /// Parse a byte array into a InstallShield Cabinet file
+ ///
+ /// Byte array to parse
+ /// Offset into the byte array
+ /// Filled cabinet on success, null on error
+ public static Header ParseCabinet(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 parse that
+ MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
+ return ParseCabinet(dataStream);
+ }
+
+ #endregion
+
+ #region Stream Data
+
+ ///
+ /// Parse a Stream into a InstallShield Cabinet file
+ ///
+ /// Stream to parse
+ /// Filled cabinet on success, null on error
+ public static Header ParseCabinet(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 cabinet to fill
+ var header = new Header();
+
+ #region Common Header
+
+ // Try to parse the cabinet header
+ var commonHeader = ParseCommonHeader(data);
+ if (commonHeader == null)
+ return null;
+
+ // Set the cabinet header
+ header.CommonHeader = commonHeader;
+
+ #endregion
+
+ #region Cabinet Descriptor
+
+ // Get the cabinet descriptor offset
+ uint cabinetDescriptorOffset = commonHeader.CabDescriptorOffset;
+ if (cabinetDescriptorOffset < 0 || cabinetDescriptorOffset >= data.Length)
+ return null;
+
+ // Seek to the cabinet descriptor
+ data.Seek(cabinetDescriptorOffset, SeekOrigin.Begin);
+
+ // Try to parse the cabinet descriptor
+ var cabinetDescriptor = ParseCabinetDescriptor(data);
+ if (cabinetDescriptor == null)
+ return null;
+
+ // Set the cabinet descriptor
+ header.CabinetDescriptor = cabinetDescriptor;
+
+ #endregion
+
+ #region File Descriptor Offsets
+
+ // Get the file table offset
+ uint fileTableOffset = commonHeader.CabDescriptorOffset + cabinetDescriptor.FileTableOffset;
+ if (fileTableOffset < 0 || fileTableOffset >= data.Length)
+ return null;
+
+ // Seek to the file table
+ data.Seek(fileTableOffset, SeekOrigin.Begin);
+
+ // Get the number of file table items
+ uint fileTableItems;
+ if (header.MajorVersion <= 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++)
+ {
+ header.FileDescriptorOffsets[i] = data.ReadUInt32();
+ }
+
+ #endregion
+
+ #region Directory Descriptors
+
+ // Create and fill the directory descriptors
+ header.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];
+
+ // If we have an invalid offset
+ if (offset < 0 || offset >= data.Length)
+ continue;
+
+ // Seek to the file descriptor offset
+ data.Seek(offset, SeekOrigin.Begin);
+
+ // Create and add the file descriptor
+ FileDescriptor directoryDescriptor = ParseDirectoryDescriptor(data, header.MajorVersion);
+ header.DirectoryDescriptors[i] = directoryDescriptor;
+ }
+
+ #endregion
+
+ #region File Descriptors
+
+ // Create and fill the file descriptors
+ header.FileDescriptors = new FileDescriptor[cabinetDescriptor.FileCount];
+ for (int i = 0; i < cabinetDescriptor.FileCount; i++)
+ {
+ // Get the file descriptor offset
+ uint offset;
+ if (header.MajorVersion <= 5)
+ {
+ offset = cabinetDescriptorOffset
+ + cabinetDescriptor.FileTableOffset
+ + header.FileDescriptorOffsets[cabinetDescriptor.DirectoryCount + i];
+ }
+ else
+ {
+ offset = cabinetDescriptorOffset
+ + cabinetDescriptor.FileTableOffset
+ + cabinetDescriptor.FileTableOffset2
+ + (uint)(i * 0x57);
+ }
+
+ // If we have an invalid offset
+ if (offset < 0 || offset >= data.Length)
+ continue;
+
+ // Seek to the file descriptor offset
+ data.Seek(offset, SeekOrigin.Begin);
+
+ // Create and add the file descriptor
+ FileDescriptor fileDescriptor = ParseFileDescriptor(data, header.MajorVersion, cabinetDescriptorOffset + cabinetDescriptor.FileTableOffset);
+ header.FileDescriptors[i] = fileDescriptor;
+ }
+
+ #endregion
+
+ #region File Group Offsets
+
+ // Create and fill the file group offsets
+ header.FileGroupOffsets = new Dictionary();
+ for (int i = 0; i < cabinetDescriptor.FileGroupOffsets.Length; i++)
+ {
+ // Get the file group offset
+ uint offset = cabinetDescriptor.FileGroupOffsets[i];
+ if (offset == 0)
+ continue;
+
+ // Adjust the file group offset
+ offset += commonHeader.CabDescriptorOffset;
+ if (offset < 0 || offset >= data.Length)
+ continue;
+
+ // Seek to the file group offset
+ data.Seek(offset, SeekOrigin.Begin);
+
+ // Create and add the offset
+ OffsetList offsetList = ParseOffsetList(data, header.MajorVersion, cabinetDescriptorOffset);
+ header.FileGroupOffsets[cabinetDescriptor.FileGroupOffsets[i]] = offsetList;
+
+ // If we have a nonzero next offset
+ uint nextOffset = offsetList.NextOffset;
+ while (nextOffset != 0)
+ {
+ // Get the next offset to read
+ uint internalOffset = nextOffset + commonHeader.CabDescriptorOffset;
+
+ // Seek to the file group offset
+ data.Seek(internalOffset, SeekOrigin.Begin);
+
+ // Create and add the offset
+ offsetList = ParseOffsetList(data, header.MajorVersion, cabinetDescriptorOffset);
+ header.FileGroupOffsets[nextOffset] = offsetList;
+
+ // Set the next offset
+ nextOffset = offsetList.NextOffset;
+ }
+ }
+
+ #endregion
+
+ #region File Groups
+
+ // Create and fill the file groups
+ List fileGroups = new List();
+ foreach (var kvp in header.FileGroupOffsets)
+ {
+ // Get the offset
+ OffsetList list = kvp.Value;
+ if (list == null)
+ continue;
+
+ /// Seek to the file group
+ data.Seek(list.DescriptorOffset + cabinetDescriptorOffset, SeekOrigin.Begin);
+
+ // Try to parse the file group
+ FileGroup fileGroup = ParseFileGroup(data, header.MajorVersion, cabinetDescriptorOffset);
+
+ // Add the file group
+ fileGroups.Add(fileGroup);
+ }
+
+ // Set the file groups
+ header.FileGroups = fileGroups.ToArray();
+
+ #endregion
+
+ #region Component Offsets
+
+ // Create and fill the component offsets
+ header.ComponentOffsets = new Dictionary();
+ for (int i = 0; i < cabinetDescriptor.ComponentOffsets.Length; i++)
+ {
+ // Get the component offset
+ uint offset = cabinetDescriptor.ComponentOffsets[i];
+ if (offset == 0)
+ continue;
+
+ // Adjust the component offset
+ offset += commonHeader.CabDescriptorOffset;
+ if (offset < 0 || offset >= data.Length)
+ continue;
+
+ // Seek to the component offset
+ data.Seek(offset, SeekOrigin.Begin);
+
+ // Create and add the offset
+ OffsetList offsetList = ParseOffsetList(data, header.MajorVersion, cabinetDescriptorOffset);
+ header.ComponentOffsets[cabinetDescriptor.ComponentOffsets[i]] = offsetList;
+
+ // If we have a nonzero next offset
+ uint nextOffset = offsetList.NextOffset;
+ while (nextOffset != 0)
+ {
+ // Get the next offset to read
+ uint internalOffset = nextOffset + commonHeader.CabDescriptorOffset;
+
+ // Seek to the file group offset
+ data.Seek(internalOffset, SeekOrigin.Begin);
+
+ // Create and add the offset
+ offsetList = ParseOffsetList(data, header.MajorVersion, cabinetDescriptorOffset);
+ header.ComponentOffsets[nextOffset] = offsetList;
+
+ // Set the next offset
+ nextOffset = offsetList.NextOffset;
+ }
+ }
+
+ #endregion
+
+ #region Components
+
+ // Create and fill the components
+ List components = new List();
+ foreach (KeyValuePair kvp in header.ComponentOffsets)
+ {
+ // Get the offset
+ OffsetList list = kvp.Value;
+ if (list == null)
+ continue;
+
+ // Seek to the component
+ data.Seek(list.DescriptorOffset + cabinetDescriptorOffset, SeekOrigin.Begin);
+
+ // Try to parse the component
+ Component component = ParseComponent(data, header.MajorVersion, cabinetDescriptorOffset);
+
+ // Add the component
+ components.Add(component);
+ }
+
+ // Set the components
+ header.Components = components.ToArray();
+
+ #endregion
+
+ return header;
+ }
+
+ ///
+ /// Parse a Stream into a common header
+ ///
+ /// Stream to parse
+ /// Filled common header on success, null on error
+ private static CommonHeader ParseCommonHeader(Stream data)
+ {
+ CommonHeader commonHeader = new CommonHeader();
+
+ commonHeader.Signature = data.ReadUInt32();
+ if (commonHeader.Signature != 0x28635349)
+ return null;
+
+ commonHeader.Version = data.ReadUInt32();
+ commonHeader.VolumeInfo = data.ReadUInt32();
+ commonHeader.CabDescriptorOffset = data.ReadUInt32();
+ commonHeader.CabDescriptorSize = data.ReadUInt32();
+
+ return commonHeader;
+ }
+
+ ///
+ /// Parse a Stream into a cabinet descriptor
+ ///
+ /// Stream to parse
+ /// Filled cabinet descriptor on success, null on error
+ private static CabDescriptor ParseCabinetDescriptor(Stream data)
+ {
+ CabDescriptor cabDescriptor = new CabDescriptor();
+
+ cabDescriptor.Reserved0 = data.ReadBytes(0x0C);
+ cabDescriptor.FileTableOffset = data.ReadUInt32();
+ cabDescriptor.Reserved1 = data.ReadBytes(0x04);
+ cabDescriptor.FileTableSize = data.ReadUInt32();
+ cabDescriptor.FileTableSize2 = data.ReadUInt32();
+ cabDescriptor.DirectoryCount = data.ReadUInt32();
+ cabDescriptor.Reserved2 = data.ReadBytes(0x08);
+ cabDescriptor.FileCount = data.ReadUInt32();
+ cabDescriptor.FileTableOffset2 = data.ReadUInt32();
+ cabDescriptor.Reserved3 = data.ReadBytes(0x0E);
+
+ // TODO: Determine how the value "71" was chosen here
+ cabDescriptor.FileGroupOffsets = new uint[71];
+ for (int i = 0; i < cabDescriptor.FileGroupOffsets.Length; i++)
+ {
+ cabDescriptor.FileGroupOffsets[i] = data.ReadUInt32();
+ }
+
+ // TODO: Determine how the value "71" was chosen here
+ cabDescriptor.ComponentOffsets = new uint[71];
+ for (int i = 0; i < cabDescriptor.ComponentOffsets.Length; i++)
+ {
+ cabDescriptor.ComponentOffsets[i] = data.ReadUInt32();
+ }
+
+ return cabDescriptor;
+ }
+
+ ///
+ /// Parse a Stream into an offset list
+ ///
+ /// Stream to parse
+ /// Major version of the cabinet
+ /// Offset of the cabinet descriptor
+ /// Filled offset list on success, null on error
+ private static OffsetList ParseOffsetList(Stream data, int majorVersion, uint descriptorOffset)
+ {
+ OffsetList offsetList = new OffsetList();
+
+ offsetList.NameOffset = data.ReadUInt32();
+ offsetList.DescriptorOffset = data.ReadUInt32();
+ offsetList.NextOffset = data.ReadUInt32();
+
+ // Cache the current offset
+ long currentOffset = data.Position;
+
+ // Seek to the name offset
+ data.Seek(offsetList.NameOffset + descriptorOffset, SeekOrigin.Begin);
+
+ // Read the string
+ if (majorVersion >= 17)
+ offsetList.Name = data.ReadString(Encoding.Unicode);
+ else
+ offsetList.Name = data.ReadString(Encoding.ASCII);
+
+ // Seek back to the correct offset
+ data.Seek(currentOffset, SeekOrigin.Begin);
+
+ return offsetList;
+ }
+
+ ///
+ /// Parse a Stream into a file group
+ ///
+ /// Stream to parse
+ /// Major version of the cabinet
+ /// Offset of the cabinet descriptor
+ /// Filled file group on success, null on error
+ private static FileGroup ParseFileGroup(Stream data, int majorVersion, uint descriptorOffset)
+ {
+ FileGroup fileGroup = new FileGroup();
+
+ fileGroup.NameOffset = data.ReadUInt32();
+
+ // Skip bytes based on the version
+ if (majorVersion <= 5)
+ _ = data.ReadBytes(0x48);
+ else
+ _ = data.ReadBytes(0x12);
+
+ fileGroup.FirstFile = data.ReadUInt16();
+ fileGroup.LastFile = data.ReadUInt32();
+
+ // Cache the current position
+ long currentPosition = data.Position;
+
+ // Read the name, if possible
+ if (fileGroup.NameOffset != 0)
+ {
+ // Seek to the name
+ data.Seek(fileGroup.NameOffset + descriptorOffset, SeekOrigin.Begin);
+
+ // Read the string
+ if (majorVersion >= 17)
+ fileGroup.Name = data.ReadString(Encoding.Unicode);
+ else
+ fileGroup.Name = data.ReadString(Encoding.ASCII);
+ }
+
+ // Seek back to the correct offset
+ data.Seek(currentPosition, SeekOrigin.Begin);
+
+ return fileGroup;
+ }
+
+ ///
+ /// Parse a Stream into a component
+ ///
+ /// Stream to parse
+ /// Major version of the cabinet
+ /// Offset of the cabinet descriptor
+ /// Filled component on success, null on error
+ private static Component ParseComponent(Stream data, int majorVersion, uint descriptorOffset)
+ {
+ Component component = new Component();
+
+ component.NameOffset = data.ReadUInt32();
+
+ // Skip bytes based on the version
+ if (majorVersion <= 5)
+ _ = data.ReadBytes(0x6C);
+ else
+ _ = data.ReadBytes(0x6B);
+
+ component.FileGroupCount = data.ReadUInt16();
+ component.FileGroupTableOffset = data.ReadUInt32();
+
+ // Cache the current position
+ long currentPosition = data.Position;
+
+ // Read the name, if possible
+ if (component.NameOffset != 0)
+ {
+ // Seek to the name
+ data.Seek(component.NameOffset + descriptorOffset, SeekOrigin.Begin);
+
+ // Read the string
+ if (majorVersion >= 17)
+ component.Name = data.ReadString(Encoding.Unicode);
+ else
+ component.Name = data.ReadString(Encoding.ASCII);
+ }
+
+ // Read the file group table, if possible
+ if (component.FileGroupCount != 0 && component.FileGroupTableOffset != 0)
+ {
+ // Seek to the file group table offset
+ data.Seek(component.FileGroupTableOffset + descriptorOffset, SeekOrigin.Begin);
+
+ // Read the file group table
+ component.FileGroupNames = new string[component.FileGroupCount];
+ for (int j = 0; j < component.FileGroupCount; j++)
+ {
+ if (majorVersion >= 17)
+ component.FileGroupNames[j] = data.ReadString(Encoding.Unicode);
+ else
+ component.FileGroupNames[j] = data.ReadString(Encoding.ASCII);
+ }
+ }
+
+ // Seek back to the correct offset
+ data.Seek(currentPosition, SeekOrigin.Begin);
+
+ return component;
+ }
+
+ ///
+ /// Parse a Stream into a directory descriptor
+ ///
+ /// Stream to parse
+ /// Major version of the cabinet
+ /// Filled directory descriptor on success, null on error
+ private static FileDescriptor ParseDirectoryDescriptor(Stream data, int majorVersion)
+ {
+ FileDescriptor fileDescriptor = new FileDescriptor();
+
+ // Read the string
+ if (majorVersion >= 17)
+ fileDescriptor.Name = data.ReadString(Encoding.Unicode);
+ else
+ fileDescriptor.Name = data.ReadString(Encoding.ASCII);
+
+ return fileDescriptor;
+ }
+
+ ///
+ /// Parse a Stream into a file descriptor
+ ///
+ /// Stream to parse
+ /// Major version of the cabinet
+ /// Offset of the cabinet descriptor
+ /// Filled file descriptor on success, null on error
+ private static FileDescriptor ParseFileDescriptor(Stream data, int majorVersion, uint descriptorOffset)
+ {
+ FileDescriptor fileDescriptor = new FileDescriptor();
+
+ // Read the descriptor based on version
+ if (majorVersion <= 5)
+ {
+ fileDescriptor.Volume = 0xFFFF; // Set by the header index
+ fileDescriptor.NameOffset = data.ReadUInt32();
+ fileDescriptor.DirectoryIndex = data.ReadUInt32();
+ fileDescriptor.Flags = (FileFlags)data.ReadUInt16();
+ fileDescriptor.ExpandedSize = data.ReadUInt32();
+ fileDescriptor.CompressedSize = data.ReadUInt32();
+ _ = data.ReadBytes(0x14); // Skip 0x14 bytes, unknown data?
+ fileDescriptor.DataOffset = data.ReadUInt32();
+
+ if (majorVersion == 5)
+ fileDescriptor.MD5 = data.ReadBytes(0x10);
+ }
+ else
+ {
+ fileDescriptor.Flags = (FileFlags)data.ReadUInt16();
+ fileDescriptor.ExpandedSize = data.ReadUInt64();
+ fileDescriptor.CompressedSize = data.ReadUInt64();
+ fileDescriptor.DataOffset = data.ReadUInt64();
+ fileDescriptor.MD5 = data.ReadBytes(0x10);
+ _ = data.ReadBytes(0x10); // Skip 0x10 bytes, unknown data?
+ fileDescriptor.NameOffset = data.ReadUInt32();
+ fileDescriptor.DirectoryIndex = data.ReadUInt16();
+ _ = data.ReadBytes(0x0C); // Skip 0x0C bytes, unknown data?
+ fileDescriptor.LinkPrevious = data.ReadUInt32();
+ fileDescriptor.LinkNext = data.ReadUInt32();
+ fileDescriptor.LinkFlags = (LinkFlags)data.ReadByteValue();
+ fileDescriptor.Volume = data.ReadUInt16();
+ }
+
+ // Cache the current position
+ long currentPosition = data.Position;
+
+ // Read the name, if possible
+ if (fileDescriptor.NameOffset != 0)
+ {
+ // Seek to the name
+ data.Seek(fileDescriptor.NameOffset + descriptorOffset, SeekOrigin.Begin);
+
+ // Read the string
+ if (majorVersion >= 17)
+ fileDescriptor.Name = data.ReadString(Encoding.Unicode);
+ else
+ fileDescriptor.Name = data.ReadString(Encoding.ASCII);
+ }
+
+ // Seek back to the correct offset
+ data.Seek(currentPosition, SeekOrigin.Begin);
+
+ return fileDescriptor;
+ }
+
+ ///
+ /// 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;
+ }
+
+ #endregion
+ }
+}
diff --git a/BurnOutSharp.Models/InstallShieldCAB/CabDescriptor.cs b/BurnOutSharp.Models/InstallShieldCAB/CabDescriptor.cs
new file mode 100644
index 00000000..27cf068e
--- /dev/null
+++ b/BurnOutSharp.Models/InstallShieldCAB/CabDescriptor.cs
@@ -0,0 +1,30 @@
+namespace BurnOutSharp.Models.InstallShieldCabinet
+{
+ ///
+ public sealed class CabDescriptor
+ {
+ public byte[] Reserved0;
+
+ public uint FileTableOffset;
+
+ public byte[] Reserved1;
+
+ public uint FileTableSize;
+
+ public uint FileTableSize2;
+
+ public uint DirectoryCount;
+
+ public byte[] Reserved2;
+
+ public uint FileCount;
+
+ public uint FileTableOffset2;
+
+ public byte[] Reserved3;
+
+ public uint[] FileGroupOffsets;
+
+ public uint[] ComponentOffsets;
+ }
+}
\ No newline at end of file
diff --git a/BurnOutSharp.Models/InstallShieldCAB/CommonHeader.cs b/BurnOutSharp.Models/InstallShieldCAB/CommonHeader.cs
new file mode 100644
index 00000000..21cf9615
--- /dev/null
+++ b/BurnOutSharp.Models/InstallShieldCAB/CommonHeader.cs
@@ -0,0 +1,16 @@
+namespace BurnOutSharp.Models.InstallShieldCabinet
+{
+ ///
+ public sealed class CommonHeader
+ {
+ public uint Signature;
+
+ public uint Version;
+
+ public uint VolumeInfo;
+
+ public uint CabDescriptorOffset;
+
+ public uint CabDescriptorSize;
+ }
+}
\ No newline at end of file
diff --git a/BurnOutSharp.Models/InstallShieldCAB/Component.cs b/BurnOutSharp.Models/InstallShieldCAB/Component.cs
new file mode 100644
index 00000000..e11ce421
--- /dev/null
+++ b/BurnOutSharp.Models/InstallShieldCAB/Component.cs
@@ -0,0 +1,16 @@
+namespace BurnOutSharp.Models.InstallShieldCabinet
+{
+ ///
+ public sealed class Component
+ {
+ public uint NameOffset;
+
+ public string Name;
+
+ public ushort FileGroupCount;
+
+ public uint FileGroupTableOffset;
+
+ public string[] FileGroupNames;
+ }
+}
\ No newline at end of file
diff --git a/BurnOutSharp.Models/InstallShieldCAB/Enums.cs b/BurnOutSharp.Models/InstallShieldCAB/Enums.cs
new file mode 100644
index 00000000..36df4d9b
--- /dev/null
+++ b/BurnOutSharp.Models/InstallShieldCAB/Enums.cs
@@ -0,0 +1,23 @@
+using System;
+
+namespace BurnOutSharp.Models.InstallShieldCabinet
+{
+ ///
+ [Flags]
+ public enum FileFlags : ushort
+ {
+ FILE_SPLIT = 1,
+ FILE_OBFUSCATED = 2,
+ FILE_COMPRESSED = 4,
+ FILE_INVALID = 8,
+ }
+
+ ///
+ public enum LinkFlags : byte
+ {
+ LINK_NONE = 0,
+ LINK_PREV = 1,
+ LINK_NEXT = 2,
+ LINK_BOTH = 3,
+ }
+}
\ No newline at end of file
diff --git a/BurnOutSharp.Models/InstallShieldCAB/FileDescriptor.cs b/BurnOutSharp.Models/InstallShieldCAB/FileDescriptor.cs
new file mode 100644
index 00000000..79266a5b
--- /dev/null
+++ b/BurnOutSharp.Models/InstallShieldCAB/FileDescriptor.cs
@@ -0,0 +1,30 @@
+namespace BurnOutSharp.Models.InstallShieldCabinet
+{
+ ///
+ public sealed class FileDescriptor
+ {
+ public uint NameOffset;
+
+ public string Name;
+
+ public uint DirectoryIndex;
+
+ public FileFlags Flags;
+
+ public ulong ExpandedSize;
+
+ public ulong CompressedSize;
+
+ public ulong DataOffset;
+
+ public byte[] MD5;
+
+ public ushort Volume;
+
+ public uint LinkPrevious;
+
+ public uint LinkNext;
+
+ public LinkFlags LinkFlags;
+ }
+}
\ No newline at end of file
diff --git a/BurnOutSharp.Models/InstallShieldCAB/FileGroup.cs b/BurnOutSharp.Models/InstallShieldCAB/FileGroup.cs
new file mode 100644
index 00000000..8e84f495
--- /dev/null
+++ b/BurnOutSharp.Models/InstallShieldCAB/FileGroup.cs
@@ -0,0 +1,14 @@
+namespace BurnOutSharp.Models.InstallShieldCabinet
+{
+ ///
+ public sealed class FileGroup
+ {
+ public uint NameOffset;
+
+ public string Name;
+
+ public uint FirstFile;
+
+ public uint LastFile;
+ }
+}
\ No newline at end of file
diff --git a/BurnOutSharp.Models/InstallShieldCAB/Header.cs b/BurnOutSharp.Models/InstallShieldCAB/Header.cs
new file mode 100644
index 00000000..8c0d690a
--- /dev/null
+++ b/BurnOutSharp.Models/InstallShieldCAB/Header.cs
@@ -0,0 +1,63 @@
+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
diff --git a/BurnOutSharp.Models/InstallShieldCAB/OffsetList.cs b/BurnOutSharp.Models/InstallShieldCAB/OffsetList.cs
new file mode 100644
index 00000000..3068f612
--- /dev/null
+++ b/BurnOutSharp.Models/InstallShieldCAB/OffsetList.cs
@@ -0,0 +1,14 @@
+namespace BurnOutSharp.Models.InstallShieldCabinet
+{
+ ///
+ public sealed class OffsetList
+ {
+ public uint NameOffset;
+
+ public string Name;
+
+ public uint DescriptorOffset;
+
+ public uint NextOffset;
+ }
+}
\ No newline at end of file
diff --git a/BurnOutSharp.Models/InstallShieldCAB/VolumeHeader.cs b/BurnOutSharp.Models/InstallShieldCAB/VolumeHeader.cs
new file mode 100644
index 00000000..b3877fb6
--- /dev/null
+++ b/BurnOutSharp.Models/InstallShieldCAB/VolumeHeader.cs
@@ -0,0 +1,38 @@
+namespace BurnOutSharp.Models.InstallShieldCabinet
+{
+ ///
+ public sealed class VolumeHeader
+ {
+ public uint DataOffset;
+
+ public uint DataOffsetHigh;
+
+ public uint FirstFileIndex;
+
+ public uint LastFileIndex;
+
+ public uint FirstFileOffset;
+
+ public uint FirstFileOffsetHigh;
+
+ public uint FirstFileSizeExpanded;
+
+ public uint FirstFileSizeExpandedHigh;
+
+ public uint FirstFileSizeCompressed;
+
+ public uint FirstFileSizeCompressedHigh;
+
+ public uint LastFileOffset;
+
+ public uint LastFileOffsetHigh;
+
+ public uint LastFileSizeExpanded;
+
+ public uint LastFileSizeExpandedHigh;
+
+ public uint LastFileSizeCompressed;
+
+ public uint LastFileSizeCompressedHigh;
+ }
+}
\ No newline at end of file