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