mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-04-24 07:03:09 +00:00
Seek -> SeekImpl
This commit is contained in:
565
BurnOutSharp/External/libgsf/GsfMSOleImpl.cs
vendored
565
BurnOutSharp/External/libgsf/GsfMSOleImpl.cs
vendored
@@ -20,61 +20,540 @@
|
||||
* USA
|
||||
*/
|
||||
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using static LibGSF.GsfUtils;
|
||||
|
||||
namespace LibGSF
|
||||
{
|
||||
public static class GsfMSOleImpl
|
||||
#region Enums
|
||||
|
||||
internal enum DIRENT_TYPE : byte
|
||||
{
|
||||
public const int OLE_HEADER_SIZE = 0x200; /* independent of big block size size */
|
||||
public const int OLE_HEADER_SIGNATURE = 0x00;
|
||||
public const int OLE_HEADER_CLSID = 0x08; /* See ReadClassStg */
|
||||
public const int OLE_HEADER_MINOR_VER = 0x18; /* 0x33 and 0x3e have been seen */
|
||||
public const int OLE_HEADER_MAJOR_VER = 0x1a; /* 0x3 been seen in wild */
|
||||
public const int OLE_HEADER_BYTE_ORDER = 0x1c; /* 0xfe 0xff == Intel Little Endian */
|
||||
public const int OLE_HEADER_BB_SHIFT = 0x1e;
|
||||
public const int OLE_HEADER_SB_SHIFT = 0x20;
|
||||
/* 0x22..0x27 reserved == 0 */
|
||||
public const int OLE_HEADER_CSECTDIR = 0x28;
|
||||
public const int OLE_HEADER_NUM_BAT = 0x2c;
|
||||
public const int OLE_HEADER_DIRENT_START = 0x30;
|
||||
/* 0x34..0x37 transacting signature must be 0 */
|
||||
public const int OLE_HEADER_THRESHOLD = 0x38;
|
||||
public const int OLE_HEADER_SBAT_START = 0x3c;
|
||||
public const int OLE_HEADER_NUM_SBAT = 0x40;
|
||||
public const int OLE_HEADER_METABAT_BLOCK = 0x44;
|
||||
public const int OLE_HEADER_NUM_METABAT = 0x48;
|
||||
public const int OLE_HEADER_START_BAT = 0x4c;
|
||||
public const int BAT_INDEX_SIZE = 4;
|
||||
public const int OLE_HEADER_METABAT_SIZE = ((OLE_HEADER_SIZE - OLE_HEADER_START_BAT) / BAT_INDEX_SIZE);
|
||||
DIRENT_TYPE_INVALID = 0,
|
||||
DIRENT_TYPE_DIR = 1,
|
||||
DIRENT_TYPE_FILE = 2,
|
||||
DIRENT_TYPE_LOCKBYTES = 3,
|
||||
DIRENT_TYPE_PROPERTY = 4,
|
||||
DIRENT_TYPE_ROOTDIR = 5,
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Classes
|
||||
|
||||
/// <summary>
|
||||
/// MS-OLE Header (.msi)
|
||||
/// </summary>
|
||||
internal class MSOleHeader
|
||||
{
|
||||
#region Constants
|
||||
|
||||
/// <summary>
|
||||
/// Independent of big block size size
|
||||
/// </summary>
|
||||
public const int OLE_HEADER_SIZE = 0x200;
|
||||
|
||||
/// <summary>
|
||||
/// OLE Signature as a byte array
|
||||
/// </summary>
|
||||
public static readonly byte[] SIGNATURE_BYTES = { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 };
|
||||
|
||||
/// <summary>
|
||||
/// OLE Signature as an unsigned Int64
|
||||
/// </summary>
|
||||
public const ulong SIGNATURE_VALUE = 0x00E11AB1A1E011CFD0;
|
||||
|
||||
#region Offsets
|
||||
|
||||
private const int OLE_HEADER_SIGNATURE = 0x00;
|
||||
|
||||
private const int OLE_HEADER_CLSID = 0x08;
|
||||
|
||||
private const int OLE_HEADER_MINOR_VER = 0x18;
|
||||
|
||||
private const int OLE_HEADER_MAJOR_VER = 0x1a;
|
||||
|
||||
private const int OLE_HEADER_BYTE_ORDER = 0x1c;
|
||||
|
||||
private const int OLE_HEADER_BB_SHIFT = 0x1e;
|
||||
|
||||
private const int OLE_HEADER_SB_SHIFT = 0x20;
|
||||
|
||||
private const int OLE_HEADER_RESERVED = 0x22;
|
||||
|
||||
private const int OLE_HEADER_CSECTDIR = 0x28;
|
||||
|
||||
private const int OLE_HEADER_NUM_BAT = 0x2c;
|
||||
|
||||
private const int OLE_HEADER_DIRENT_START = 0x30;
|
||||
|
||||
private const int OLE_HEADER_TRANSACTING_SIGNATURE = 0x34;
|
||||
|
||||
private const int OLE_HEADER_THRESHOLD = 0x38;
|
||||
|
||||
private const int OLE_HEADER_SBAT_START = 0x3c;
|
||||
|
||||
private const int OLE_HEADER_NUM_SBAT = 0x40;
|
||||
|
||||
private const int OLE_HEADER_METABAT_BLOCK = 0x44;
|
||||
|
||||
private const int OLE_HEADER_NUM_METABAT = 0x48;
|
||||
|
||||
private const int OLE_HEADER_START_BAT = 0x4c;
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <remarks>0x00</remarks>
|
||||
public byte[] SIGNATURE { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// See ReadClassStg
|
||||
/// </summary>
|
||||
/// <remarks>0x08</remarks>
|
||||
public byte[] CLSID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 0x33 and 0x3E have been seen
|
||||
/// </summary>
|
||||
/// <remarks>0x18</remarks>
|
||||
public ushort MINOR_VER { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 0x03 been seen in wild
|
||||
/// </summary>
|
||||
/// <remarks>0x1A</remarks>
|
||||
public ushort MAJOR_VER { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 0xFE 0xFF == Intel Little Endian
|
||||
/// </summary>
|
||||
/// <remarks>0x1C</remarks>
|
||||
public ushort BYTE_ORDER { get; set; }
|
||||
|
||||
/// <remarks>0x1E</remarks>
|
||||
public ushort BB_SHIFT { get; set; }
|
||||
|
||||
/// <remarks>0x20</remarks>
|
||||
public ushort SB_SHIFT { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 0x22..0x27 reserved == 0
|
||||
/// </summary>
|
||||
/// <remarks>0x22</remarks>
|
||||
public byte[] RESERVED { get; set; }
|
||||
|
||||
/// <remarks>0x28</remarks>
|
||||
public uint CSECTDIR { get; set; }
|
||||
|
||||
/// <remarks>0x2C</remarks>
|
||||
public uint NUM_BAT { get; set; }
|
||||
|
||||
/// <remarks>0x30</remarks>
|
||||
public uint DIRENT_START { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 0x34..0x37 transacting signature must be 0
|
||||
/// </summary>
|
||||
/// <remarks>0x34</remarks>
|
||||
public uint TRANSACTING_SIGNATURE { get; set; }
|
||||
|
||||
/// <remarks>0x38</remarks>
|
||||
public uint THRESHOLD { get; set; }
|
||||
|
||||
/// <remarks>0x3C</remarks>
|
||||
public uint SBAT_START { get; set; }
|
||||
|
||||
/// <remarks>0x40</remarks>
|
||||
public uint NUM_SBAT { get; set; }
|
||||
|
||||
/// <remarks>0x44</remarks>
|
||||
public uint METABAT_BLOCK { get; set; }
|
||||
|
||||
/// <remarks>0x48</remarks>
|
||||
public uint NUM_METABAT { get; set; }
|
||||
|
||||
/// <remarks>0x4C</remarks>
|
||||
public uint START_BAT { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Derived Properties
|
||||
|
||||
public int BB_SIZE => 1 << BB_SHIFT;
|
||||
|
||||
public int BB_FILTER => BB_SIZE << 1;
|
||||
|
||||
public int SB_SIZE => 1 << SB_SHIFT;
|
||||
|
||||
public int SB_FILTER => SB_SIZE << 1;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private MSOleHeader() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new default MSOleHeader
|
||||
/// </summary>
|
||||
public static MSOleHeader CreateDefault()
|
||||
{
|
||||
MSOleHeader header = new MSOleHeader();
|
||||
|
||||
header.SIGNATURE = SIGNATURE_BYTES;
|
||||
header.CLSID = new byte[16];
|
||||
header.MINOR_VER = 0x003E;
|
||||
header.MAJOR_VER = 0x0003;
|
||||
header.BYTE_ORDER = 0xFFFE;
|
||||
header.BB_SHIFT = 0x0009;
|
||||
header.SB_SHIFT = 0x0006;
|
||||
header.RESERVED = new byte[6];
|
||||
header.CSECTDIR = 0x00000000;
|
||||
header.NUM_BAT = 0x00000000;
|
||||
header.DIRENT_START = 0xFFFFFFFF;
|
||||
header.TRANSACTING_SIGNATURE = 0x00000000;
|
||||
header.THRESHOLD = 0x00001000;
|
||||
|
||||
// Remainder have default values for their types
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new MSOleHeader from data
|
||||
/// </summary>
|
||||
public static MSOleHeader Create(byte[] data, int ptr, ref Exception err)
|
||||
{
|
||||
if (data == null || data.Length < OLE_HEADER_SIZE)
|
||||
return null;
|
||||
|
||||
MSOleHeader header = new MSOleHeader();
|
||||
|
||||
header.SIGNATURE = new byte[8];
|
||||
Array.Copy(data, ptr + OLE_HEADER_SIGNATURE, header.SIGNATURE, 0, 8);
|
||||
|
||||
if (!header.SIGNATURE.SequenceEqual(SIGNATURE_BYTES))
|
||||
{
|
||||
err = new ArgumentException("Signature is malformed");
|
||||
return null;
|
||||
}
|
||||
|
||||
header.CLSID = new byte[16];
|
||||
Array.Copy(data, ptr + OLE_HEADER_CLSID, header.CLSID, 0, 16);
|
||||
header.MINOR_VER = GSF_LE_GET_GUINT16(data, ptr + OLE_HEADER_MINOR_VER);
|
||||
header.MAJOR_VER = GSF_LE_GET_GUINT16(data, ptr + OLE_HEADER_MAJOR_VER);
|
||||
header.BYTE_ORDER = GSF_LE_GET_GUINT16(data, ptr + OLE_HEADER_BYTE_ORDER);
|
||||
header.BB_SHIFT = GSF_LE_GET_GUINT16(data, ptr + OLE_HEADER_BB_SHIFT);
|
||||
header.SB_SHIFT = GSF_LE_GET_GUINT16(data, ptr + OLE_HEADER_SB_SHIFT);
|
||||
header.RESERVED = new byte[6];
|
||||
Array.Copy(data, ptr + OLE_HEADER_RESERVED, header.RESERVED, 0, 6);
|
||||
header.CSECTDIR = GSF_LE_GET_GUINT32(data, ptr + OLE_HEADER_CSECTDIR);
|
||||
header.NUM_BAT = GSF_LE_GET_GUINT32(data, ptr + OLE_HEADER_NUM_BAT);
|
||||
header.DIRENT_START = GSF_LE_GET_GUINT32(data, ptr + OLE_HEADER_DIRENT_START);
|
||||
header.TRANSACTING_SIGNATURE = GSF_LE_GET_GUINT32(data, ptr + OLE_HEADER_TRANSACTING_SIGNATURE);
|
||||
|
||||
if (header.TRANSACTING_SIGNATURE != 0)
|
||||
{
|
||||
err = new ArgumentException("Transacting signature must be 0");
|
||||
return null;
|
||||
}
|
||||
|
||||
header.THRESHOLD = GSF_LE_GET_GUINT32(data, ptr + OLE_HEADER_THRESHOLD);
|
||||
header.SBAT_START = GSF_LE_GET_GUINT32(data, ptr + OLE_HEADER_SBAT_START);
|
||||
header.NUM_SBAT = GSF_LE_GET_GUINT32(data, ptr + OLE_HEADER_NUM_SBAT);
|
||||
header.METABAT_BLOCK = GSF_LE_GET_GUINT32(data, ptr + OLE_HEADER_METABAT_BLOCK);
|
||||
header.NUM_METABAT = GSF_LE_GET_GUINT32(data, ptr + OLE_HEADER_NUM_METABAT);
|
||||
header.START_BAT = GSF_LE_GET_GUINT32(data, ptr + OLE_HEADER_START_BAT);
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write to data from an existing MSOleHeader
|
||||
/// </summary>
|
||||
public bool Write(byte[] data, int ptr)
|
||||
{
|
||||
if (data == null || data.Length < OLE_HEADER_SIZE)
|
||||
return false;
|
||||
|
||||
Array.Copy(SIGNATURE, 0, data, ptr + OLE_HEADER_SIGNATURE, SIGNATURE.Length);
|
||||
Array.Copy(CLSID, 0, data, ptr + OLE_HEADER_CLSID, CLSID.Length);
|
||||
GSF_LE_SET_GUINT16(data, ptr + OLE_HEADER_MINOR_VER, MINOR_VER);
|
||||
GSF_LE_SET_GUINT16(data, ptr + OLE_HEADER_MAJOR_VER, MAJOR_VER);
|
||||
GSF_LE_SET_GUINT16(data, ptr + OLE_HEADER_BYTE_ORDER, BYTE_ORDER);
|
||||
GSF_LE_SET_GUINT16(data, ptr + OLE_HEADER_BB_SHIFT, BB_SHIFT);
|
||||
GSF_LE_SET_GUINT16(data, ptr + OLE_HEADER_SB_SHIFT, SB_SHIFT);
|
||||
Array.Copy(RESERVED, 0, data, ptr + OLE_HEADER_RESERVED, RESERVED.Length);
|
||||
GSF_LE_SET_GUINT32(data, ptr + OLE_HEADER_CSECTDIR, CSECTDIR);
|
||||
GSF_LE_SET_GUINT32(data, ptr + OLE_HEADER_NUM_BAT, NUM_BAT);
|
||||
GSF_LE_SET_GUINT32(data, ptr + OLE_HEADER_DIRENT_START, DIRENT_START);
|
||||
GSF_LE_SET_GUINT32(data, ptr + OLE_HEADER_TRANSACTING_SIGNATURE, TRANSACTING_SIGNATURE);
|
||||
GSF_LE_SET_GUINT32(data, ptr + OLE_HEADER_THRESHOLD, THRESHOLD);
|
||||
GSF_LE_SET_GUINT32(data, ptr + OLE_HEADER_SBAT_START, SBAT_START);
|
||||
GSF_LE_SET_GUINT32(data, ptr + OLE_HEADER_NUM_SBAT, NUM_SBAT);
|
||||
GSF_LE_SET_GUINT32(data, ptr + OLE_HEADER_METABAT_BLOCK, METABAT_BLOCK);
|
||||
GSF_LE_SET_GUINT32(data, ptr + OLE_HEADER_START_BAT, START_BAT);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// MS-OLE Directory Entry (.msi)
|
||||
/// </summary>
|
||||
internal class MSOleDirectoryEntry
|
||||
{
|
||||
#region Constants
|
||||
|
||||
public const int DIRENT_MAX_NAME_SIZE = 0x40;
|
||||
public const int DIRENT_DETAILS_SIZE = 0x40;
|
||||
public const int DIRENT_SIZE = (DIRENT_MAX_NAME_SIZE + DIRENT_DETAILS_SIZE);
|
||||
public const int DIRENT_NAME_LEN = 0x40; /* length in bytes incl 0 terminator */
|
||||
public const int DIRENT_TYPE = 0x42;
|
||||
public const int DIRENT_COLOUR = 0x43;
|
||||
public const int DIRENT_PREV = 0x44;
|
||||
public const int DIRENT_NEXT = 0x48;
|
||||
public const int DIRENT_CHILD = 0x4c;
|
||||
public const int DIRENT_CLSID = 0x50; /* only for dirs */
|
||||
public const int DIRENT_USERFLAGS = 0x60; /* only for dirs */
|
||||
public const int DIRENT_CREATE_TIME = 0x64; /* for files */
|
||||
public const int DIRENT_MODIFY_TIME = 0x6c; /* for files */
|
||||
public const int DIRENT_FIRSTBLOCK = 0x74;
|
||||
public const int DIRENT_FILE_SIZE = 0x78;
|
||||
/* 0x7c..0x7f reserved == 0 */
|
||||
|
||||
public const int DIRENT_TYPE_INVALID = 0;
|
||||
public const int DIRENT_TYPE_DIR = 1;
|
||||
public const int DIRENT_TYPE_FILE = 2;
|
||||
public const int DIRENT_TYPE_LOCKBYTES = 3; /* ? */
|
||||
public const int DIRENT_TYPE_PROPERTY = 4; /* ? */
|
||||
public const int DIRENT_TYPE_ROOTDIR = 5;
|
||||
public const int DIRENT_DETAILS_SIZE = 0x40;
|
||||
|
||||
public const int DIRENT_SIZE = (DIRENT_MAX_NAME_SIZE + DIRENT_DETAILS_SIZE);
|
||||
|
||||
public const uint DIRENT_MAGIC_END = 0xffffffff;
|
||||
|
||||
#region Offsets
|
||||
|
||||
private const int DIRENT_NAME = 0x00;
|
||||
|
||||
private const int DIRENT_NAME_LEN = 0x40;
|
||||
|
||||
private const int DIRENT_TYPE_FLAG = 0x42;
|
||||
|
||||
private const int DIRENT_COLOR = 0x43;
|
||||
|
||||
private const int DIRENT_PREV = 0x44;
|
||||
|
||||
private const int DIRENT_NEXT = 0x48;
|
||||
|
||||
private const int DIRENT_CHILD = 0x4c;
|
||||
|
||||
private const int DIRENT_CLSID = 0x50;
|
||||
|
||||
private const int DIRENT_USERFLAGS = 0x60;
|
||||
|
||||
private const int DIRENT_CREATE_TIME = 0x64;
|
||||
|
||||
private const int DIRENT_MODIFY_TIME = 0x6C;
|
||||
|
||||
private const int DIRENT_FIRSTBLOCK = 0x74;
|
||||
|
||||
private const int DIRENT_FILE_SIZE = 0x78;
|
||||
|
||||
private const int DIRENT_RESERVED = 0x7C;
|
||||
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
/// <remarks>0x00</remarks>
|
||||
public byte[] NAME { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Length in bytes incl 0 terminator
|
||||
/// </summary>
|
||||
/// <remarks>0x40</remarks>
|
||||
public ushort NAME_LEN { get; set; }
|
||||
|
||||
/// <remarks>0x42</remarks>
|
||||
public DIRENT_TYPE TYPE_FLAG { get; set; }
|
||||
|
||||
/// <remarks>0x43</remarks>
|
||||
public byte COLOR { get; set; }
|
||||
|
||||
/// <remarks>0x44</remarks>
|
||||
public uint PREV { get; set; }
|
||||
|
||||
/// <remarks>0x48</remarks>
|
||||
public uint NEXT { get; set; }
|
||||
|
||||
/// <remarks>0x4C</remarks>
|
||||
public uint CHILD { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Only for dirs
|
||||
/// </summary>
|
||||
/// <remarks>0x50</remarks>
|
||||
public byte[] CLSID { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Only for dirs
|
||||
/// </summary>
|
||||
/// <remarks>0x60</remarks>
|
||||
public uint USERFLAGS { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// For files
|
||||
/// </summary>
|
||||
/// <remarks>0x64</remarks>
|
||||
public ulong CREATE_TIME { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// For files
|
||||
/// </summary>
|
||||
/// <remarks>0x6C</remarks>
|
||||
public ulong MODIFY_TIME { get; set; }
|
||||
|
||||
/// <remarks>0x74</remarks>
|
||||
public uint FIRSTBLOCK { get; set; }
|
||||
|
||||
/// <remarks>0x78</remarks>
|
||||
public uint FILE_SIZE { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 0x7c..0x7f reserved == 0
|
||||
/// </summary>
|
||||
/// <remarks>0x7C</remarks>
|
||||
public uint RESERVED { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Derived Properties
|
||||
|
||||
public string NAME_STRING
|
||||
{
|
||||
get
|
||||
{
|
||||
if (NAME_LEN <= 0 || NAME_LEN > DIRENT_MAX_NAME_SIZE)
|
||||
return null;
|
||||
|
||||
// !#%!@$#^
|
||||
// Sometimes, rarely, people store the stream name as ascii
|
||||
// rather than utf16. Do a validation first just in case.
|
||||
int end;
|
||||
try { end = new UnicodeEncoding(false, true).GetCharCount(NAME); }
|
||||
catch { end = -1; }
|
||||
|
||||
if (end == -1)
|
||||
{
|
||||
byte[] direntNameBytes = Encoding.Convert(Encoding.ASCII, Encoding.UTF8, NAME);
|
||||
return Encoding.UTF8.GetString(direntNameBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] direntNameBytes = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, NAME);
|
||||
return Encoding.UTF8.GetString(direntNameBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public bool IS_DIRECTORY => TYPE_FLAG != DIRENT_TYPE.DIRENT_TYPE_FILE;
|
||||
|
||||
#endregion
|
||||
|
||||
#region Constructor
|
||||
|
||||
/// <summary>
|
||||
/// Private constructor
|
||||
/// </summary>
|
||||
private MSOleDirectoryEntry() { }
|
||||
|
||||
/// <summary>
|
||||
/// Create a new default MSOleDirectoryEntry
|
||||
/// </summary>
|
||||
public static MSOleDirectoryEntry CreateDefault()
|
||||
{
|
||||
// TODO: Figure out if there are any sane defaults
|
||||
return new MSOleDirectoryEntry();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create a new MSOleDirectoryEntry from data
|
||||
/// </summary>
|
||||
public static MSOleDirectoryEntry Create(byte[] data, int ptr, ref Exception err)
|
||||
{
|
||||
if (data == null || data.Length < DIRENT_SIZE)
|
||||
return null;
|
||||
|
||||
MSOleDirectoryEntry directoryEntry = new MSOleDirectoryEntry();
|
||||
|
||||
directoryEntry.NAME = new byte[DIRENT_MAX_NAME_SIZE];
|
||||
Array.Copy(data, ptr + DIRENT_NAME, directoryEntry.NAME, 0, directoryEntry.NAME.Length);
|
||||
directoryEntry.NAME_LEN = GSF_LE_GET_GUINT16(data, ptr + DIRENT_NAME_LEN);
|
||||
directoryEntry.TYPE_FLAG = (DIRENT_TYPE)GSF_LE_GET_GUINT8(data, ptr + DIRENT_TYPE_FLAG);
|
||||
|
||||
if (directoryEntry.TYPE_FLAG != DIRENT_TYPE.DIRENT_TYPE_DIR
|
||||
&& directoryEntry.TYPE_FLAG != DIRENT_TYPE.DIRENT_TYPE_FILE
|
||||
&& directoryEntry.TYPE_FLAG != DIRENT_TYPE.DIRENT_TYPE_ROOTDIR)
|
||||
{
|
||||
err = new Exception($"Unknown stream type 0x{directoryEntry.TYPE_FLAG:x}");
|
||||
return null;
|
||||
}
|
||||
|
||||
directoryEntry.COLOR = GSF_LE_GET_GUINT8(data, ptr + DIRENT_COLOR);
|
||||
directoryEntry.PREV = GSF_LE_GET_GUINT32(data, ptr + DIRENT_PREV);
|
||||
directoryEntry.NEXT = GSF_LE_GET_GUINT32(data, ptr + DIRENT_NEXT);
|
||||
directoryEntry.CHILD = GSF_LE_GET_GUINT32(data, ptr + DIRENT_CHILD);
|
||||
directoryEntry.CLSID = new byte[0x10];
|
||||
Array.Copy(data, ptr + DIRENT_CLSID, directoryEntry.CLSID, 0, directoryEntry.CLSID.Length);
|
||||
directoryEntry.USERFLAGS = GSF_LE_GET_GUINT32(data, ptr + DIRENT_USERFLAGS);
|
||||
directoryEntry.CREATE_TIME = GSF_LE_GET_GUINT64(data, ptr + DIRENT_CREATE_TIME);
|
||||
directoryEntry.MODIFY_TIME = GSF_LE_GET_GUINT64(data, ptr + DIRENT_MODIFY_TIME);
|
||||
directoryEntry.FIRSTBLOCK = GSF_LE_GET_GUINT32(data, ptr + DIRENT_FIRSTBLOCK);
|
||||
directoryEntry.FILE_SIZE = GSF_LE_GET_GUINT32(data, ptr + DIRENT_FILE_SIZE);
|
||||
directoryEntry.RESERVED = GSF_LE_GET_GUINT32(data, ptr + DIRENT_RESERVED);
|
||||
|
||||
return directoryEntry;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Write to data from an existing MSOleHeader
|
||||
/// </summary>
|
||||
public bool Write(byte[] data, int ptr)
|
||||
{
|
||||
if (data == null || data.Length < DIRENT_SIZE)
|
||||
return false;
|
||||
|
||||
Array.Copy(NAME, 0, data, ptr + DIRENT_NAME, NAME.Length);
|
||||
GSF_LE_SET_GUINT16(data, ptr + DIRENT_NAME_LEN, NAME_LEN);
|
||||
GSF_LE_SET_GUINT8(data, ptr + DIRENT_TYPE_FLAG, (byte)TYPE_FLAG);
|
||||
GSF_LE_SET_GUINT8(data, ptr + DIRENT_COLOR, COLOR);
|
||||
GSF_LE_SET_GUINT32(data, ptr + DIRENT_PREV, PREV);
|
||||
GSF_LE_SET_GUINT32(data, ptr + DIRENT_NEXT, NEXT);
|
||||
GSF_LE_SET_GUINT32(data, ptr + DIRENT_CHILD, CHILD);
|
||||
Array.Copy(CLSID, 0, data, ptr + DIRENT_CLSID, CLSID.Length);
|
||||
GSF_LE_SET_GUINT32(data, ptr + DIRENT_USERFLAGS, USERFLAGS);
|
||||
GSF_LE_SET_GUINT64(data, ptr + DIRENT_CREATE_TIME, CREATE_TIME);
|
||||
GSF_LE_SET_GUINT64(data, ptr + DIRENT_MODIFY_TIME, MODIFY_TIME);
|
||||
GSF_LE_SET_GUINT32(data, ptr + DIRENT_FIRSTBLOCK, FIRSTBLOCK);
|
||||
GSF_LE_SET_GUINT32(data, ptr + DIRENT_FILE_SIZE, FILE_SIZE);
|
||||
GSF_LE_SET_GUINT32(data, ptr + DIRENT_RESERVED, RESERVED);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
public static class GsfMSOleImpl
|
||||
{
|
||||
public const int OLE_HEADER_CSECTDIR = 0x28;
|
||||
public const int OLE_HEADER_NUM_BAT = 0x2C;
|
||||
public const int OLE_HEADER_SBAT_START = 0x3C;
|
||||
public const int OLE_HEADER_START_BAT = 0x4C;
|
||||
public const int BAT_INDEX_SIZE = 4;
|
||||
public const int OLE_HEADER_METABAT_SIZE = ((MSOleHeader.OLE_HEADER_SIZE - OLE_HEADER_START_BAT) / BAT_INDEX_SIZE);
|
||||
|
||||
/* flags in the block allocation list to denote special blocks */
|
||||
public const uint BAT_MAGIC_UNUSED = 0xffffffff; /* -1 */
|
||||
public const uint BAT_MAGIC_END_OF_CHAIN = 0xfffffffe; /* -2 */
|
||||
public const uint BAT_MAGIC_BAT = 0xfffffffd; /* a bat block, -3 */
|
||||
public const uint BAT_MAGIC_METABAT = 0xfffffffc; /* a metabat block -4 */
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
187
BurnOutSharp/External/libgsf/Input/GsfInfileMSOle.cs
vendored
187
BurnOutSharp/External/libgsf/Input/GsfInfileMSOle.cs
vendored
@@ -27,8 +27,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using static LibGSF.GsfMSOleImpl;
|
||||
using static LibGSF.GsfUtils;
|
||||
|
||||
@@ -130,7 +128,7 @@ namespace LibGSF.Input
|
||||
|
||||
public bool IsDirectory { get; set; }
|
||||
|
||||
public List<MSOleDirent> Children { get; set; }
|
||||
public List<MSOleDirent> Children { get; set; } = new List<MSOleDirent>();
|
||||
|
||||
/// <summary>
|
||||
/// 16 byte GUID used by some apps
|
||||
@@ -343,7 +341,7 @@ namespace LibGSF.Input
|
||||
//if (FALSE && first_block != last_block)
|
||||
// Console.Error.WriteLine($"Check if {first_block}-{last_block} of {Bat.NumBlocks} are contiguous.");
|
||||
|
||||
while (++i <= last_block && ++raw_block == Bat.Block[i]);
|
||||
while (++i <= last_block && ++raw_block == Bat.Block[i]) ;
|
||||
|
||||
if (i > last_block)
|
||||
{
|
||||
@@ -388,7 +386,7 @@ namespace LibGSF.Input
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Seek(long offset, SeekOrigin whence)
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence)
|
||||
{
|
||||
CurBlock = BAT_MAGIC_UNUSED;
|
||||
return false;
|
||||
@@ -469,7 +467,7 @@ namespace LibGSF.Input
|
||||
|
||||
// OLE_HEADER_SIZE is fixed at 512, but the sector containing the
|
||||
// header is padded out to BigBlock.Size (sector size) when BigBlock.Size > 512.
|
||||
if (Input.Seek(Math.Max(OLE_HEADER_SIZE, Info.BigBlock.Size) + (block << Info.BigBlock.Shift) + offset, SeekOrigin.Begin))
|
||||
if (Input.Seek(Math.Max(MSOleHeader.OLE_HEADER_SIZE, Info.BigBlock.Size) + (block << Info.BigBlock.Shift) + offset, SeekOrigin.Begin))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@@ -495,7 +493,7 @@ namespace LibGSF.Input
|
||||
/// either from the OLE header, or a meta-bat block.
|
||||
/// </summary>
|
||||
/// <returns>A pointer to the element after the last position filled</returns>
|
||||
private uint[] ReadMetabat(uint[] bats, int batsPtr, uint max_bat, uint[] metabat, uint metabat_end)
|
||||
private int? ReadMetabat(uint[] bats, int batsPtr, uint max_bat, uint[] metabat, uint metabat_end)
|
||||
{
|
||||
for (int metabatPtr = 0; metabatPtr < metabat_end; metabatPtr++)
|
||||
{
|
||||
@@ -530,7 +528,7 @@ namespace LibGSF.Input
|
||||
}
|
||||
}
|
||||
|
||||
return bats;
|
||||
return batsPtr;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -538,8 +536,7 @@ namespace LibGSF.Input
|
||||
/// </summary>
|
||||
private static void GetUnsignedInts(uint[] dst, byte[] src, int srcPtr, int num_bytes)
|
||||
{
|
||||
int dstPtr = 0;
|
||||
for (; (num_bytes -= BAT_INDEX_SIZE) >= 0; srcPtr += BAT_INDEX_SIZE)
|
||||
for (int dstPtr = 0; (num_bytes -= BAT_INDEX_SIZE) >= 0; srcPtr += BAT_INDEX_SIZE)
|
||||
{
|
||||
dst[dstPtr++] = GSF_LE_GET_GUINT32(src, srcPtr);
|
||||
}
|
||||
@@ -582,13 +579,13 @@ namespace LibGSF.Input
|
||||
/// parent is optional.
|
||||
private MSOleDirent CreateDirectoryEntry(uint entry, MSOleDirent parent, bool[] seen_before)
|
||||
{
|
||||
if (entry >= DIRENT_MAGIC_END)
|
||||
if (entry >= MSOleDirectoryEntry.DIRENT_MAGIC_END)
|
||||
return null;
|
||||
|
||||
if (entry > uint.MaxValue / DIRENT_SIZE)
|
||||
if (entry > uint.MaxValue / MSOleDirectoryEntry.DIRENT_SIZE)
|
||||
return null;
|
||||
|
||||
uint block = ((entry * DIRENT_SIZE) >> Info.BigBlock.Shift);
|
||||
uint block = ((entry * MSOleDirectoryEntry.DIRENT_SIZE) >> Info.BigBlock.Shift);
|
||||
if (block >= Bat.NumBlocks)
|
||||
return null;
|
||||
|
||||
@@ -602,70 +599,44 @@ namespace LibGSF.Input
|
||||
return null;
|
||||
|
||||
int dataPtr = 0; // data[0]
|
||||
dataPtr += (int)((DIRENT_SIZE * entry) % Info.BigBlock.Size);
|
||||
dataPtr += (int)((MSOleDirectoryEntry.DIRENT_SIZE * entry) % Info.BigBlock.Size);
|
||||
|
||||
byte type = GSF_LE_GET_GUINT8(data, dataPtr + DIRENT_TYPE);
|
||||
if (type != DIRENT_TYPE_DIR && type != DIRENT_TYPE_FILE && type != DIRENT_TYPE_ROOTDIR)
|
||||
Exception err = null;
|
||||
MSOleDirectoryEntry directoryEntry = MSOleDirectoryEntry.Create(data, dataPtr, ref err);
|
||||
if (err != null)
|
||||
{
|
||||
Console.Error.WriteLine($"Unknown stream type 0x{type:x}");
|
||||
Console.Error.WriteLine(err.Message);
|
||||
return null;
|
||||
}
|
||||
|
||||
if (parent == null && type != DIRENT_TYPE_ROOTDIR)
|
||||
if (parent == null && directoryEntry.TYPE_FLAG != DIRENT_TYPE.DIRENT_TYPE_ROOTDIR)
|
||||
{
|
||||
// See bug 346118.
|
||||
Console.Error.WriteLine("Root directory is not marked as such.");
|
||||
type = DIRENT_TYPE_ROOTDIR;
|
||||
directoryEntry.TYPE_FLAG = DIRENT_TYPE.DIRENT_TYPE_ROOTDIR;
|
||||
}
|
||||
|
||||
// It looks like directory (and root directory) sizes are sometimes bogus
|
||||
uint size = GSF_LE_GET_GUINT32(data, dataPtr + DIRENT_FILE_SIZE);
|
||||
if (!(type == DIRENT_TYPE_DIR || type == DIRENT_TYPE_ROOTDIR || size <= (uint)Input.Size))
|
||||
if (!(directoryEntry.TYPE_FLAG == DIRENT_TYPE.DIRENT_TYPE_DIR || directoryEntry.TYPE_FLAG == DIRENT_TYPE.DIRENT_TYPE_ROOTDIR || directoryEntry.FILE_SIZE <= (uint)Input.Size))
|
||||
return null;
|
||||
|
||||
ulong ft = GSF_LE_GET_GUINT64(data, dataPtr + DIRENT_MODIFY_TIME);
|
||||
|
||||
MSOleDirent dirent = new MSOleDirent
|
||||
{
|
||||
Index = (int)entry,
|
||||
Size = (int)size,
|
||||
ModTime = DateTimeFromFileTime(ft),
|
||||
Size = (int)directoryEntry.FILE_SIZE,
|
||||
ModTime = DateTimeFromFileTime(directoryEntry.MODIFY_TIME),
|
||||
|
||||
// Store the class id which is 16 byte identifier used by some apps
|
||||
ClassID = directoryEntry.CLSID,
|
||||
|
||||
// Root dir is always big block
|
||||
UseSmallBlock = parent != null && (directoryEntry.FILE_SIZE < Info.Threshold),
|
||||
FirstBlock = directoryEntry.FIRSTBLOCK,
|
||||
IsDirectory = directoryEntry.IS_DIRECTORY,
|
||||
Children = new List<MSOleDirent>(),
|
||||
};
|
||||
|
||||
// Store the class id which is 16 byte identifier used by some apps
|
||||
Array.Copy(data, dataPtr + DIRENT_CLSID, dirent.ClassID, 0, dirent.ClassID.Length);
|
||||
|
||||
// Root dir is always big block
|
||||
dirent.UseSmallBlock = parent != null && (size < Info.Threshold);
|
||||
dirent.FirstBlock = GSF_LE_GET_GUINT32(data, dataPtr + DIRENT_FIRSTBLOCK);
|
||||
dirent.IsDirectory = (type != DIRENT_TYPE_FILE);
|
||||
dirent.Children = null;
|
||||
|
||||
uint prev = GSF_LE_GET_GUINT32(data, dataPtr + DIRENT_PREV);
|
||||
uint next = GSF_LE_GET_GUINT32(data, dataPtr + DIRENT_NEXT);
|
||||
uint child = GSF_LE_GET_GUINT32(data, dataPtr + DIRENT_CHILD);
|
||||
ushort name_len = GSF_LE_GET_GUINT16(data, dataPtr + DIRENT_NAME_LEN);
|
||||
|
||||
dirent.Name = null;
|
||||
if (0 < name_len && name_len <= DIRENT_MAX_NAME_SIZE)
|
||||
{
|
||||
// !#%!@$#^
|
||||
// Sometimes, rarely, people store the stream name as ascii
|
||||
// rather than utf16. Do a validation first just in case.
|
||||
int end;
|
||||
try { end = new UTF8Encoding(false, true).GetCharCount(data); }
|
||||
catch { end = -1; }
|
||||
|
||||
if (end == -1 || (end + 1) != name_len)
|
||||
{
|
||||
byte[] direntNameBytes = Encoding.Convert(Encoding.ASCII, Encoding.UTF8, data, 0, end);
|
||||
dirent.Name = Encoding.UTF8.GetString(direntNameBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
dirent.Name = Encoding.UTF8.GetString(data, 0, end + 1);
|
||||
}
|
||||
}
|
||||
dirent.Name = directoryEntry.NAME_STRING.TrimEnd('\0');
|
||||
|
||||
// Be really anal in the face of screwups
|
||||
if (dirent.Name == null)
|
||||
@@ -680,12 +651,12 @@ namespace LibGSF.Input
|
||||
}
|
||||
|
||||
// NOTE : These links are a tree, not a linked list
|
||||
CreateDirectoryEntry(prev, parent, seen_before);
|
||||
CreateDirectoryEntry(next, parent, seen_before);
|
||||
CreateDirectoryEntry(directoryEntry.PREV, parent, seen_before);
|
||||
CreateDirectoryEntry(directoryEntry.NEXT, parent, seen_before);
|
||||
|
||||
if (dirent.IsDirectory)
|
||||
CreateDirectoryEntry(child, dirent, seen_before);
|
||||
else if (child != DIRENT_MAGIC_END)
|
||||
CreateDirectoryEntry(directoryEntry.CHILD, dirent, seen_before);
|
||||
else if (directoryEntry.CHILD != MSOleDirectoryEntry.DIRENT_MAGIC_END)
|
||||
Console.Error.WriteLine("A non directory stream with children ?");
|
||||
|
||||
return dirent;
|
||||
@@ -721,43 +692,29 @@ namespace LibGSF.Input
|
||||
/// <returns>True on error setting <paramref name="err"/> if it is supplied.</returns>
|
||||
private bool InitInfo(ref Exception err)
|
||||
{
|
||||
byte[] header;
|
||||
|
||||
// Check the header
|
||||
byte[] signature = { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 };
|
||||
if (Input.Seek(0, SeekOrigin.Begin)
|
||||
|| (header = Input.Read(OLE_HEADER_SIZE, null)) == null
|
||||
|| !new ReadOnlySpan<byte>(header, 0, signature.Length).SequenceEqual(signature))
|
||||
// Seek to the header
|
||||
if (Input.Seek(0, SeekOrigin.Begin))
|
||||
{
|
||||
err = new Exception("No OLE2 signature");
|
||||
err = new Exception("Cannot seek to header");
|
||||
return true;
|
||||
}
|
||||
|
||||
ushort bb_shift = GSF_LE_GET_GUINT16(header, OLE_HEADER_BB_SHIFT);
|
||||
ushort sb_shift = GSF_LE_GET_GUINT16(header, OLE_HEADER_SB_SHIFT);
|
||||
uint num_bat = GSF_LE_GET_GUINT32(header, OLE_HEADER_NUM_BAT);
|
||||
uint num_sbat = GSF_LE_GET_GUINT32(header, OLE_HEADER_NUM_SBAT);
|
||||
uint threshold = GSF_LE_GET_GUINT32(header, OLE_HEADER_THRESHOLD);
|
||||
uint dirent_start = GSF_LE_GET_GUINT32(header, OLE_HEADER_DIRENT_START);
|
||||
uint metabat_block = GSF_LE_GET_GUINT32(header, OLE_HEADER_METABAT_BLOCK);
|
||||
uint num_metabat = GSF_LE_GET_GUINT32(header, OLE_HEADER_NUM_METABAT);
|
||||
byte[] header = Input.Read(MSOleHeader.OLE_HEADER_SIZE, null);
|
||||
if (header == null)
|
||||
{
|
||||
err = new Exception("Header could not be read");
|
||||
return true;
|
||||
}
|
||||
|
||||
//if (gsf_debug_flag("OLE2"))
|
||||
//{
|
||||
// Console.Error.WriteLine($"bb_shift=%d (size=%d)", bb_shift, 1 << bb_shift);
|
||||
// Console.Error.WriteLine($"sb_shift=%d (size=%d)", sb_shift, 1 << sb_shift);
|
||||
// Console.Error.WriteLine($"num_bat=%d (0x%08x)", num_bat, num_bat);
|
||||
// Console.Error.WriteLine($"num_sbat=%d (0x%08x)", num_sbat, num_sbat);
|
||||
// Console.Error.WriteLine($"threshold=%d (0x%08x)", threshold, threshold);
|
||||
// Console.Error.WriteLine($"dirent_start=0x%08x", dirent_start);
|
||||
// Console.Error.WriteLine($"num_metabat=%d (0x%08x)", num_metabat, num_metabat);
|
||||
//}
|
||||
MSOleHeader headerImpl = MSOleHeader.Create(header, 0, ref err);
|
||||
if (headerImpl == null)
|
||||
return true;
|
||||
|
||||
// Some sanity checks
|
||||
// 1) There should always be at least 1 BAT block
|
||||
// 2) It makes no sense to have a block larger than 2^31 for now.
|
||||
// Maybe relax this later, but not much.
|
||||
if (6 > bb_shift || bb_shift >= 31 || sb_shift > bb_shift || (Input.Size >> bb_shift) < 1)
|
||||
if (6 > headerImpl.BB_SHIFT || headerImpl.BB_SHIFT >= 31 || headerImpl.SB_SHIFT > headerImpl.BB_SHIFT || (Input.Size >> headerImpl.BB_SHIFT) < 1)
|
||||
{
|
||||
err = new Exception("Unreasonable block sizes");
|
||||
return true;
|
||||
@@ -765,21 +722,21 @@ namespace LibGSF.Input
|
||||
|
||||
MSOleInfo info = new MSOleInfo
|
||||
{
|
||||
BigBlock = new MSOleInfo.MSOleInfoBlock(),
|
||||
SmallBlock = new MSOleInfo.MSOleInfoBlock(),
|
||||
BigBlock = new MSOleInfo.MSOleInfoBlock { Bat = new MSOleBAT() },
|
||||
SmallBlock = new MSOleInfo.MSOleInfoBlock { Bat = new MSOleBAT() },
|
||||
};
|
||||
|
||||
info.RefCount = 1;
|
||||
info.BigBlock.Shift = bb_shift;
|
||||
info.BigBlock.Size = 1 << info.BigBlock.Shift;
|
||||
info.BigBlock.Filter = info.BigBlock.Size << 1;
|
||||
info.SmallBlock.Shift = sb_shift;
|
||||
info.SmallBlock.Size = 1 << info.SmallBlock.Shift;
|
||||
info.SmallBlock.Filter = info.SmallBlock.Size << 1;
|
||||
info.Threshold = threshold;
|
||||
info.SBatStart = GSF_LE_GET_GUINT32(header, OLE_HEADER_SBAT_START);
|
||||
info.NumSbat = num_sbat;
|
||||
info.MaxBlock = (Input.Size - OLE_HEADER_SIZE + info.BigBlock.Size - 1) / info.BigBlock.Size;
|
||||
info.BigBlock.Shift = headerImpl.BB_SHIFT;
|
||||
info.BigBlock.Size = headerImpl.BB_SIZE;
|
||||
info.BigBlock.Filter = headerImpl.BB_FILTER;
|
||||
info.SmallBlock.Shift = headerImpl.SB_SHIFT;
|
||||
info.SmallBlock.Size = headerImpl.SB_SIZE;
|
||||
info.SmallBlock.Filter = headerImpl.SB_FILTER;
|
||||
info.Threshold = headerImpl.THRESHOLD;
|
||||
info.SBatStart = headerImpl.SBAT_START;
|
||||
info.NumSbat = headerImpl.NUM_SBAT;
|
||||
info.MaxBlock = (Input.Size - MSOleHeader.OLE_HEADER_SIZE + info.BigBlock.Size - 1) / info.BigBlock.Size;
|
||||
info.SmallBlockFile = null;
|
||||
|
||||
Info = info;
|
||||
@@ -789,18 +746,19 @@ namespace LibGSF.Input
|
||||
|
||||
uint[] metabat = null;
|
||||
uint last;
|
||||
uint[] ptr;
|
||||
int? ptr = null;
|
||||
|
||||
// Very rough heuristic, just in case
|
||||
if (num_bat < info.MaxBlock && info.NumSbat < info.MaxBlock)
|
||||
uint num_bat = headerImpl.NUM_BAT;
|
||||
if (num_bat < info.MaxBlock && headerImpl.NUM_SBAT < info.MaxBlock)
|
||||
{
|
||||
info.BigBlock.Bat.NumBlocks = (uint)(num_bat * (info.BigBlock.Size / BAT_INDEX_SIZE));
|
||||
info.BigBlock.Bat.NumBlocks = (uint)(num_bat * (headerImpl.BB_SIZE / BAT_INDEX_SIZE));
|
||||
info.BigBlock.Bat.Block = new uint[info.BigBlock.Bat.NumBlocks];
|
||||
|
||||
metabat = new uint[Math.Max(info.BigBlock.Size, OLE_HEADER_SIZE)];
|
||||
metabat = new uint[Math.Max(headerImpl.BB_SIZE, MSOleHeader.OLE_HEADER_SIZE)];
|
||||
|
||||
// Reading the elements invalidates this memory, make copy
|
||||
GetUnsignedInts(metabat, header, OLE_HEADER_START_BAT, OLE_HEADER_SIZE - OLE_HEADER_START_BAT);
|
||||
GetUnsignedInts(metabat, header, OLE_HEADER_START_BAT, MSOleHeader.OLE_HEADER_SIZE - OLE_HEADER_START_BAT);
|
||||
last = num_bat;
|
||||
if (last > OLE_HEADER_METABAT_SIZE)
|
||||
last = OLE_HEADER_METABAT_SIZE;
|
||||
@@ -808,12 +766,9 @@ namespace LibGSF.Input
|
||||
ptr = ReadMetabat(info.BigBlock.Bat.Block, 0, info.BigBlock.Bat.NumBlocks, metabat, last);
|
||||
num_bat -= last;
|
||||
}
|
||||
else
|
||||
{
|
||||
ptr = null;
|
||||
}
|
||||
|
||||
int ptrPtr = 0; // ptr[0]
|
||||
uint metabat_block = headerImpl.METABAT_BLOCK;
|
||||
uint num_metabat = headerImpl.NUM_METABAT;
|
||||
|
||||
last = (uint)((info.BigBlock.Size - BAT_INDEX_SIZE) / BAT_INDEX_SIZE);
|
||||
while (ptr != null && num_metabat-- > 0)
|
||||
@@ -854,13 +809,11 @@ namespace LibGSF.Input
|
||||
num_bat -= last;
|
||||
}
|
||||
|
||||
ptr = ReadMetabat(ptr, ptrPtr, info.BigBlock.Bat.NumBlocks, metabat, last);
|
||||
ptr = ReadMetabat(info.BigBlock.Bat.Block, ptr.Value, info.BigBlock.Bat.NumBlocks, metabat, last);
|
||||
}
|
||||
|
||||
bool fail = (ptr == null);
|
||||
|
||||
metabat = ptr = null;
|
||||
|
||||
if (fail)
|
||||
{
|
||||
err = new Exception("Inconsistent block allocation table");
|
||||
@@ -868,7 +821,7 @@ namespace LibGSF.Input
|
||||
}
|
||||
|
||||
// Read the directory's bat, we do not know the size
|
||||
if (MSOleBAT.Create(Info.BigBlock.Bat, 0, dirent_start, out MSOleBAT tempBat))
|
||||
if (MSOleBAT.Create(Info.BigBlock.Bat, 0, headerImpl.DIRENT_START, out MSOleBAT tempBat))
|
||||
{
|
||||
err = new Exception("Problems making block allocation table");
|
||||
return true;
|
||||
@@ -877,7 +830,7 @@ namespace LibGSF.Input
|
||||
Bat = tempBat;
|
||||
|
||||
// Read the directory
|
||||
bool[] seen_before = new bool[(Bat.NumBlocks << info.BigBlock.Shift) * DIRENT_SIZE + 1];
|
||||
bool[] seen_before = new bool[(Bat.NumBlocks << info.BigBlock.Shift) * MSOleDirectoryEntry.DIRENT_SIZE + 1];
|
||||
DirectoryEntry = info.RootDir = CreateDirectoryEntry(0, null, seen_before);
|
||||
if (DirectoryEntry == null)
|
||||
{
|
||||
|
||||
@@ -199,7 +199,7 @@ namespace LibGSF.Input
|
||||
protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0) => null;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Seek(long offset, SeekOrigin whence) => false;
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override GsfInput ChildByIndex(int i, ref Exception error)
|
||||
|
||||
@@ -269,7 +269,7 @@ namespace LibGSF.Input
|
||||
private static bool warned = false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Seek(long offset, SeekOrigin whence)
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence)
|
||||
{
|
||||
long pos = offset;
|
||||
|
||||
|
||||
@@ -176,7 +176,7 @@ namespace LibGSF.Input
|
||||
/// the end of the stream, or to the current location.
|
||||
/// </param>
|
||||
/// <returns>True on error.</returns>
|
||||
public virtual bool Seek(long offset, SeekOrigin whence)
|
||||
public bool Seek(long offset, SeekOrigin whence)
|
||||
{
|
||||
long pos = offset;
|
||||
|
||||
@@ -196,7 +196,7 @@ namespace LibGSF.Input
|
||||
if (pos == CurrentOffset)
|
||||
return false;
|
||||
|
||||
if (Seek(offset, whence))
|
||||
if (SeekImpl(offset, whence))
|
||||
return true;
|
||||
|
||||
CurrentOffset = pos;
|
||||
@@ -524,6 +524,8 @@ namespace LibGSF.Input
|
||||
|
||||
protected virtual byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0) => null;
|
||||
|
||||
protected virtual bool SeekImpl(long offset, SeekOrigin whence) => false;
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@@ -261,7 +261,7 @@ namespace LibGSF.Input
|
||||
private static bool warned = false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Seek(long offset, SeekOrigin whence)
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence)
|
||||
{
|
||||
long pos = offset;
|
||||
|
||||
|
||||
@@ -162,7 +162,7 @@ namespace LibGSF.Input
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Seek(long offset, SeekOrigin whence)
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence)
|
||||
{
|
||||
if (Stream == null)
|
||||
return true;
|
||||
|
||||
@@ -138,7 +138,7 @@ namespace LibGSF.Input
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Seek(long offset, SeekOrigin whence) => false;
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => false;
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
@@ -96,7 +96,7 @@ namespace LibGSF.Input
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Seek(long offset, SeekOrigin whence) => false;
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => false;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -104,7 +104,7 @@ namespace LibGSF.Input
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Seek(long offset, SeekOrigin whence) => false;
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => false;
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
@@ -206,7 +206,7 @@ namespace LibGSF.Input
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Seek(long offset, SeekOrigin whence)
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence)
|
||||
{
|
||||
if (File == null)
|
||||
return true;
|
||||
|
||||
@@ -85,7 +85,7 @@ namespace LibGSF.Input
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Seek(long offset, SeekOrigin whence)
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence)
|
||||
{
|
||||
Remainder = null;
|
||||
bool res = Source.Seek(offset, whence);
|
||||
|
||||
@@ -90,7 +90,7 @@ namespace LibGSF.Input
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override bool Seek(long offset, SeekOrigin whence) => false;
|
||||
protected override bool SeekImpl(long offset, SeekOrigin whence) => false;
|
||||
|
||||
/// <inheritdoc/>
|
||||
public override int NumChildren() => Children != null ? Children.Length : -1;
|
||||
|
||||
@@ -192,19 +192,20 @@ namespace LibGSF.Output
|
||||
ole.RegisterChild(ole);
|
||||
|
||||
// Build the header
|
||||
byte[] buf = Enumerable.Repeat<byte>(0xFF, OLE_HEADER_SIZE).ToArray();
|
||||
Array.Copy(default_header, buf, default_header.Length);
|
||||
byte[] buf = Enumerable.Repeat<byte>(0xFF, MSOleHeader.OLE_HEADER_SIZE).ToArray();
|
||||
|
||||
GSF_LE_SET_GUINT16(buf, OLE_HEADER_BB_SHIFT, (ushort)ole.BigBlock.Shift);
|
||||
GSF_LE_SET_GUINT16(buf, OLE_HEADER_SB_SHIFT, (ushort)ole.SmallBlock.Shift);
|
||||
MSOleHeader header = MSOleHeader.CreateDefault();
|
||||
header.BB_SHIFT = (ushort)ole.BigBlock.Shift;
|
||||
header.SB_SHIFT = (ushort)ole.SmallBlock.Shift;
|
||||
|
||||
// 4k sector OLE files seen in the wild have version 4
|
||||
if (ole.BigBlock.Size == 4096)
|
||||
GSF_LE_SET_GUINT16(buf, OLE_HEADER_MAJOR_VER, 4);
|
||||
header.MAJOR_VER = 4;
|
||||
|
||||
sink.Write(OLE_HEADER_SIZE, buf);
|
||||
header.Write(buf, 0);
|
||||
sink.Write(MSOleHeader.OLE_HEADER_SIZE, buf);
|
||||
|
||||
// Header must be padded out to bb.size with zeros
|
||||
// Header must be padded out to BigBlock.Size with zeros
|
||||
ole.PadZero();
|
||||
|
||||
return ole;
|
||||
@@ -476,7 +477,7 @@ namespace LibGSF.Output
|
||||
/// pad_zero to move to the start of the next block, then get the block number.
|
||||
/// This avoids fence post type problems with partial blocks.
|
||||
/// </summary>
|
||||
private int CurrentBlock() => (int)((Sink.CurrentOffset - OLE_HEADER_SIZE) >> BigBlock.Shift);
|
||||
private int CurrentBlock() => (int)((Sink.CurrentOffset - MSOleHeader.OLE_HEADER_SIZE) >> BigBlock.Shift);
|
||||
|
||||
private int BytesLeftInBlock()
|
||||
{
|
||||
@@ -534,8 +535,6 @@ namespace LibGSF.Output
|
||||
|
||||
if (bufi != 0)
|
||||
sink.Write(bufi * BAT_INDEX_SIZE, buf);
|
||||
|
||||
bufi = 0;
|
||||
}
|
||||
|
||||
private static void WriteConst(GsfOutput sink, uint value, int n)
|
||||
@@ -574,7 +573,7 @@ namespace LibGSF.Output
|
||||
/// </summary>
|
||||
private bool WriteDirectory()
|
||||
{
|
||||
byte[] buf = new byte[OLE_HEADER_SIZE];
|
||||
byte[] buf = new byte[MSOleHeader.OLE_HEADER_SIZE];
|
||||
uint next;
|
||||
uint xbat_pos;
|
||||
int metabat_size = BigBlock.Size / BAT_INDEX_SIZE - 1;
|
||||
@@ -631,15 +630,15 @@ namespace LibGSF.Output
|
||||
PadBatUnused(0);
|
||||
int num_sbat = CurrentBlock() - sbat_start;
|
||||
|
||||
int name_len = 0;
|
||||
|
||||
// Write dirents
|
||||
int dirent_start = CurrentBlock();
|
||||
for (int i = 0; i < elem.Count; i++)
|
||||
{
|
||||
GsfOutfileMSOle child = elem[i];
|
||||
|
||||
buf = Enumerable.Repeat<byte>(0, DIRENT_SIZE).ToArray();
|
||||
buf = Enumerable.Repeat<byte>(0, MSOleDirectoryEntry.DIRENT_SIZE).ToArray();
|
||||
|
||||
MSOleDirectoryEntry directoryEntry = MSOleDirectoryEntry.CreateDefault();
|
||||
|
||||
// Hard code 'Root Entry' for the root
|
||||
if (i == 0 || child.Name != null)
|
||||
@@ -648,39 +647,27 @@ namespace LibGSF.Output
|
||||
|
||||
byte[] nameUtf16Bytes = Encoding.UTF8.GetBytes(name);
|
||||
nameUtf16Bytes = Encoding.Convert(Encoding.UTF8, Encoding.Unicode, nameUtf16Bytes);
|
||||
string nameUtf16 = Encoding.Unicode.GetString(nameUtf16Bytes);
|
||||
name_len = nameUtf16.Length;
|
||||
if (name_len >= DIRENT_MAX_NAME_SIZE)
|
||||
name_len = DIRENT_MAX_NAME_SIZE - 1;
|
||||
ushort name_len = (ushort)nameUtf16Bytes.Length;
|
||||
if (name_len >= MSOleDirectoryEntry.DIRENT_MAX_NAME_SIZE)
|
||||
name_len = MSOleDirectoryEntry.DIRENT_MAX_NAME_SIZE - 1;
|
||||
|
||||
// Be wary about endianness
|
||||
for (int j = 0; j < name_len; j++)
|
||||
{
|
||||
GSF_LE_SET_GUINT16(buf, j * 2, nameUtf16[j]);
|
||||
}
|
||||
|
||||
name_len++;
|
||||
directoryEntry.NAME = nameUtf16Bytes;
|
||||
directoryEntry.NAME_LEN = (ushort)(name_len + 1);
|
||||
}
|
||||
|
||||
GSF_LE_SET_GUINT16(buf, DIRENT_NAME_LEN, (ushort)(name_len * 2));
|
||||
|
||||
if (child.Root == child)
|
||||
{
|
||||
GSF_LE_SET_GUINT8(buf, DIRENT_TYPE, DIRENT_TYPE_ROOTDIR);
|
||||
GSF_LE_SET_GUINT32(buf, DIRENT_FIRSTBLOCK, (sb_data_size > 0) ? (uint)sb_data_start : BAT_MAGIC_END_OF_CHAIN);
|
||||
GSF_LE_SET_GUINT32(buf, DIRENT_FILE_SIZE, (uint)sb_data_size);
|
||||
|
||||
// Write the class id
|
||||
Array.Copy(child.ClassID, 0, buf, DIRENT_CLSID, child.ClassID.Length);
|
||||
directoryEntry.TYPE_FLAG = DIRENT_TYPE.DIRENT_TYPE_ROOTDIR;
|
||||
directoryEntry.FIRSTBLOCK = (sb_data_size > 0) ? (uint)sb_data_start : BAT_MAGIC_END_OF_CHAIN;
|
||||
directoryEntry.FILE_SIZE = (uint)sb_data_size;
|
||||
directoryEntry.CLSID = child.ClassID;
|
||||
}
|
||||
else if (child.Type == MSOleOutfileType.MSOLE_DIR)
|
||||
{
|
||||
GSF_LE_SET_GUINT8(buf, DIRENT_TYPE, DIRENT_TYPE_DIR);
|
||||
GSF_LE_SET_GUINT32(buf, DIRENT_FIRSTBLOCK, BAT_MAGIC_END_OF_CHAIN);
|
||||
GSF_LE_SET_GUINT32(buf, DIRENT_FILE_SIZE, 0);
|
||||
|
||||
// Write the class id
|
||||
Array.Copy(child.ClassID, 0, buf, DIRENT_CLSID, child.ClassID.Length);
|
||||
directoryEntry.TYPE_FLAG = DIRENT_TYPE.DIRENT_TYPE_DIR;
|
||||
directoryEntry.FIRSTBLOCK = BAT_MAGIC_END_OF_CHAIN;
|
||||
directoryEntry.FILE_SIZE = 0;
|
||||
directoryEntry.CLSID = child.ClassID;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -688,18 +675,19 @@ namespace LibGSF.Output
|
||||
if (size != child.Parent.CurrentSize)
|
||||
Console.Error.WriteLine("File too big");
|
||||
|
||||
GSF_LE_SET_GUINT8(buf, DIRENT_TYPE, DIRENT_TYPE_FILE);
|
||||
GSF_LE_SET_GUINT32(buf, DIRENT_FIRSTBLOCK, (uint)child.FirstBlock);
|
||||
GSF_LE_SET_GUINT32(buf, DIRENT_FILE_SIZE, size);
|
||||
directoryEntry.TYPE_FLAG = DIRENT_TYPE.DIRENT_TYPE_FILE;
|
||||
directoryEntry.FIRSTBLOCK = (uint)child.FirstBlock;
|
||||
directoryEntry.FILE_SIZE = size;
|
||||
directoryEntry.CLSID = new byte[0x10];
|
||||
}
|
||||
|
||||
GSF_LE_SET_GUINT64(buf, DIRENT_MODIFY_TIME, (ulong)(child.ModTime?.ToFileTime() ?? 0));
|
||||
directoryEntry.MODIFY_TIME = (ulong)(child.ModTime?.ToFileTime() ?? 0);
|
||||
|
||||
// Make everything black (red == 0)
|
||||
GSF_LE_SET_GUINT8(buf, DIRENT_COLOUR, 1);
|
||||
directoryEntry.COLOR = 1;
|
||||
|
||||
GsfOutfileMSOle tmp = child.Container as GsfOutfileMSOle;
|
||||
next = DIRENT_MAGIC_END;
|
||||
next = MSOleDirectoryEntry.DIRENT_MAGIC_END;
|
||||
if (child.Root != child && tmp != null)
|
||||
{
|
||||
for (int j = 0; j < tmp.Content_Dir_Children.Count; j++)
|
||||
@@ -719,19 +707,20 @@ namespace LibGSF.Output
|
||||
}
|
||||
|
||||
// Make linked list rather than tree, only use next
|
||||
GSF_LE_SET_GUINT32(buf, DIRENT_PREV, DIRENT_MAGIC_END);
|
||||
GSF_LE_SET_GUINT32(buf, DIRENT_NEXT, next);
|
||||
directoryEntry.PREV = MSOleDirectoryEntry.DIRENT_MAGIC_END;
|
||||
directoryEntry.NEXT = next;
|
||||
|
||||
uint child_index = DIRENT_MAGIC_END;
|
||||
uint child_index = MSOleDirectoryEntry.DIRENT_MAGIC_END;
|
||||
if (child.Type == MSOleOutfileType.MSOLE_DIR && child.Content_Dir_Children != null)
|
||||
{
|
||||
GsfOutfileMSOle first = child.Content_Dir_Children[0];
|
||||
child_index = (uint)first.ChildIndex;
|
||||
}
|
||||
|
||||
GSF_LE_SET_GUINT32(buf, DIRENT_CHILD, child_index);
|
||||
directoryEntry.CHILD = child_index;
|
||||
|
||||
Sink.Write(DIRENT_SIZE, buf);
|
||||
directoryEntry.Write(buf, 0);
|
||||
Sink.Write(MSOleDirectoryEntry.DIRENT_SIZE, buf);
|
||||
}
|
||||
|
||||
PadZero();
|
||||
@@ -765,7 +754,7 @@ namespace LibGSF.Output
|
||||
// by _tell and .cur_size may be out of sync. We don't
|
||||
// want to loop forever here.
|
||||
|
||||
long i = ((Sink.CurrentSize + BAT_INDEX_SIZE * (num_bat + num_xbat) - OLE_HEADER_SIZE - 1) >> BigBlock.Shift) + 1;
|
||||
long i = ((Sink.CurrentSize + BAT_INDEX_SIZE * (num_bat + num_xbat) - MSOleHeader.OLE_HEADER_SIZE - 1) >> BigBlock.Shift) + 1;
|
||||
i -= bat_start;
|
||||
if (num_bat != i)
|
||||
{
|
||||
@@ -800,8 +789,6 @@ namespace LibGSF.Output
|
||||
blocks = (int)num_bat;
|
||||
}
|
||||
|
||||
byte[] outerTemp;
|
||||
|
||||
// Fix up the header
|
||||
if (BigBlock.Size == 4096)
|
||||
{
|
||||
|
||||
@@ -157,13 +157,13 @@ namespace LibMSI
|
||||
|
||||
internal int MediaTransformDiskId { get; set; }
|
||||
|
||||
internal LinkedList<LibmsiTable> Tables { get; set; }
|
||||
internal LinkedList<LibmsiTable> Tables { get; set; } = new LinkedList<LibmsiTable>();
|
||||
|
||||
internal LinkedList<LibmsiTransform> Transforms { get; set; }
|
||||
internal LinkedList<LibmsiTransform> Transforms { get; set; } = new LinkedList<LibmsiTransform>();
|
||||
|
||||
internal LinkedList<LibmsiStream> Streams { get; set; }
|
||||
internal LinkedList<LibmsiStream> Streams { get; set; } = new LinkedList<LibmsiStream>();
|
||||
|
||||
internal LinkedList<LibmsiStorage> Storages { get; set; }
|
||||
internal LinkedList<LibmsiStorage> Storages { get; set; } = new LinkedList<LibmsiStorage>();
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
Reference in New Issue
Block a user