mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-09-22 06:45:03 +00:00
Add IS-CAB models and builder
This commit is contained in:
651
BurnOutSharp.Builders/InstallShieldCabinet.cs
Normal file
651
BurnOutSharp.Builders/InstallShieldCabinet.cs
Normal file
@@ -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
|
||||
|
||||
/// <summary>
|
||||
/// Parse a byte array into a InstallShield Cabinet file
|
||||
/// </summary>
|
||||
/// <param name="data">Byte array to parse</param>
|
||||
/// <param name="offset">Offset into the byte array</param>
|
||||
/// <returns>Filled cabinet on success, null on error</returns>
|
||||
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
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a InstallShield Cabinet file
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled cabinet on success, null on error</returns>
|
||||
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<long, OffsetList>();
|
||||
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<FileGroup> fileGroups = new List<FileGroup>();
|
||||
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<long, OffsetList>();
|
||||
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<Component> components = new List<Component>();
|
||||
foreach (KeyValuePair<long, OffsetList> 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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a common header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled common header on success, null on error</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a cabinet descriptor
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <returns>Filled cabinet descriptor on success, null on error</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into an offset list
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="majorVersion">Major version of the cabinet</param>
|
||||
/// <param name="descriptorOffset">Offset of the cabinet descriptor</param>
|
||||
/// <returns>Filled offset list on success, null on error</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a file group
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="majorVersion">Major version of the cabinet</param>
|
||||
/// <param name="descriptorOffset">Offset of the cabinet descriptor</param>
|
||||
/// <returns>Filled file group on success, null on error</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a component
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="majorVersion">Major version of the cabinet</param>
|
||||
/// <param name="descriptorOffset">Offset of the cabinet descriptor</param>
|
||||
/// <returns>Filled component on success, null on error</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a directory descriptor
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="majorVersion">Major version of the cabinet</param>
|
||||
/// <returns>Filled directory descriptor on success, null on error</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a file descriptor
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="majorVersion">Major version of the cabinet</param>
|
||||
/// <param name="descriptorOffset">Offset of the cabinet descriptor</param>
|
||||
/// <returns>Filled file descriptor on success, null on error</returns>
|
||||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Parse a Stream into a volume header
|
||||
/// </summary>
|
||||
/// <param name="data">Stream to parse</param>
|
||||
/// <param name="majorVersion">Major version of the cabinet</param>
|
||||
/// <returns>Filled volume header on success, null on error</returns>
|
||||
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
|
||||
}
|
||||
}
|
||||
30
BurnOutSharp.Models/InstallShieldCAB/CabDescriptor.cs
Normal file
30
BurnOutSharp.Models/InstallShieldCAB/CabDescriptor.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
namespace BurnOutSharp.Models.InstallShieldCabinet
|
||||
{
|
||||
/// <see href="https://github.com/twogood/unshield/blob/main/lib/cabfile.h"/>
|
||||
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;
|
||||
}
|
||||
}
|
||||
16
BurnOutSharp.Models/InstallShieldCAB/CommonHeader.cs
Normal file
16
BurnOutSharp.Models/InstallShieldCAB/CommonHeader.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace BurnOutSharp.Models.InstallShieldCabinet
|
||||
{
|
||||
/// <see href="https://github.com/twogood/unshield/blob/main/lib/cabfile.h"/>
|
||||
public sealed class CommonHeader
|
||||
{
|
||||
public uint Signature;
|
||||
|
||||
public uint Version;
|
||||
|
||||
public uint VolumeInfo;
|
||||
|
||||
public uint CabDescriptorOffset;
|
||||
|
||||
public uint CabDescriptorSize;
|
||||
}
|
||||
}
|
||||
16
BurnOutSharp.Models/InstallShieldCAB/Component.cs
Normal file
16
BurnOutSharp.Models/InstallShieldCAB/Component.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
namespace BurnOutSharp.Models.InstallShieldCabinet
|
||||
{
|
||||
/// <see href="https://github.com/twogood/unshield/blob/main/lib/libunshield.h"/>
|
||||
public sealed class Component
|
||||
{
|
||||
public uint NameOffset;
|
||||
|
||||
public string Name;
|
||||
|
||||
public ushort FileGroupCount;
|
||||
|
||||
public uint FileGroupTableOffset;
|
||||
|
||||
public string[] FileGroupNames;
|
||||
}
|
||||
}
|
||||
23
BurnOutSharp.Models/InstallShieldCAB/Enums.cs
Normal file
23
BurnOutSharp.Models/InstallShieldCAB/Enums.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
using System;
|
||||
|
||||
namespace BurnOutSharp.Models.InstallShieldCabinet
|
||||
{
|
||||
/// <see href="https://github.com/twogood/unshield/blob/main/lib/cabfile.h"/>
|
||||
[Flags]
|
||||
public enum FileFlags : ushort
|
||||
{
|
||||
FILE_SPLIT = 1,
|
||||
FILE_OBFUSCATED = 2,
|
||||
FILE_COMPRESSED = 4,
|
||||
FILE_INVALID = 8,
|
||||
}
|
||||
|
||||
/// <see href="https://github.com/twogood/unshield/blob/main/lib/cabfile.h"/>
|
||||
public enum LinkFlags : byte
|
||||
{
|
||||
LINK_NONE = 0,
|
||||
LINK_PREV = 1,
|
||||
LINK_NEXT = 2,
|
||||
LINK_BOTH = 3,
|
||||
}
|
||||
}
|
||||
30
BurnOutSharp.Models/InstallShieldCAB/FileDescriptor.cs
Normal file
30
BurnOutSharp.Models/InstallShieldCAB/FileDescriptor.cs
Normal file
@@ -0,0 +1,30 @@
|
||||
namespace BurnOutSharp.Models.InstallShieldCabinet
|
||||
{
|
||||
/// <see href="https://github.com/twogood/unshield/blob/main/lib/cabfile.h"/>
|
||||
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;
|
||||
}
|
||||
}
|
||||
14
BurnOutSharp.Models/InstallShieldCAB/FileGroup.cs
Normal file
14
BurnOutSharp.Models/InstallShieldCAB/FileGroup.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace BurnOutSharp.Models.InstallShieldCabinet
|
||||
{
|
||||
/// <see href="https://github.com/twogood/unshield/blob/main/lib/libunshield.h"/>
|
||||
public sealed class FileGroup
|
||||
{
|
||||
public uint NameOffset;
|
||||
|
||||
public string Name;
|
||||
|
||||
public uint FirstFile;
|
||||
|
||||
public uint LastFile;
|
||||
}
|
||||
}
|
||||
63
BurnOutSharp.Models/InstallShieldCAB/Header.cs
Normal file
63
BurnOutSharp.Models/InstallShieldCAB/Header.cs
Normal file
@@ -0,0 +1,63 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace BurnOutSharp.Models.InstallShieldCabinet
|
||||
{
|
||||
/// <see href="https://github.com/twogood/unshield/blob/main/lib/internal.h"/>
|
||||
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<long, OffsetList> FileGroupOffsets { get; set; }
|
||||
|
||||
public FileGroup[] FileGroups { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Components
|
||||
|
||||
public Dictionary<long, OffsetList> ComponentOffsets { get; set; }
|
||||
|
||||
public Component[] Components { get; set; }
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
14
BurnOutSharp.Models/InstallShieldCAB/OffsetList.cs
Normal file
14
BurnOutSharp.Models/InstallShieldCAB/OffsetList.cs
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace BurnOutSharp.Models.InstallShieldCabinet
|
||||
{
|
||||
/// <see href="https://github.com/twogood/unshield/blob/main/lib/cabfile.h"/>
|
||||
public sealed class OffsetList
|
||||
{
|
||||
public uint NameOffset;
|
||||
|
||||
public string Name;
|
||||
|
||||
public uint DescriptorOffset;
|
||||
|
||||
public uint NextOffset;
|
||||
}
|
||||
}
|
||||
38
BurnOutSharp.Models/InstallShieldCAB/VolumeHeader.cs
Normal file
38
BurnOutSharp.Models/InstallShieldCAB/VolumeHeader.cs
Normal file
@@ -0,0 +1,38 @@
|
||||
namespace BurnOutSharp.Models.InstallShieldCabinet
|
||||
{
|
||||
/// <see href="https://github.com/twogood/unshield/blob/main/lib/cabfile.h"/>
|
||||
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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user