diff --git a/BurnOutSharp/External/libgsf/GsfMSOleImpl.cs b/BurnOutSharp/External/libgsf/GsfMSOleImpl.cs index b7344410..01b05a71 100644 --- a/BurnOutSharp/External/libgsf/GsfMSOleImpl.cs +++ b/BurnOutSharp/External/libgsf/GsfMSOleImpl.cs @@ -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 + + /// + /// MS-OLE Header (.msi) + /// + internal class MSOleHeader + { + #region Constants + + /// + /// Independent of big block size size + /// + public const int OLE_HEADER_SIZE = 0x200; + + /// + /// OLE Signature as a byte array + /// + public static readonly byte[] SIGNATURE_BYTES = { 0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1 }; + + /// + /// OLE Signature as an unsigned Int64 + /// + 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 + + /// 0x00 + public byte[] SIGNATURE { get; set; } + + /// + /// See ReadClassStg + /// + /// 0x08 + public byte[] CLSID { get; set; } + + /// + /// 0x33 and 0x3E have been seen + /// + /// 0x18 + public ushort MINOR_VER { get; set; } + + /// + /// 0x03 been seen in wild + /// + /// 0x1A + public ushort MAJOR_VER { get; set; } + + /// + /// 0xFE 0xFF == Intel Little Endian + /// + /// 0x1C + public ushort BYTE_ORDER { get; set; } + + /// 0x1E + public ushort BB_SHIFT { get; set; } + + /// 0x20 + public ushort SB_SHIFT { get; set; } + + /// + /// 0x22..0x27 reserved == 0 + /// + /// 0x22 + public byte[] RESERVED { get; set; } + + /// 0x28 + public uint CSECTDIR { get; set; } + + /// 0x2C + public uint NUM_BAT { get; set; } + + /// 0x30 + public uint DIRENT_START { get; set; } + + /// + /// 0x34..0x37 transacting signature must be 0 + /// + /// 0x34 + public uint TRANSACTING_SIGNATURE { get; set; } + + /// 0x38 + public uint THRESHOLD { get; set; } + + /// 0x3C + public uint SBAT_START { get; set; } + + /// 0x40 + public uint NUM_SBAT { get; set; } + + /// 0x44 + public uint METABAT_BLOCK { get; set; } + + /// 0x48 + public uint NUM_METABAT { get; set; } + + /// 0x4C + 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 + + /// + /// Private constructor + /// + private MSOleHeader() { } + + /// + /// Create a new default MSOleHeader + /// + 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; + } + + /// + /// Create a new MSOleHeader from data + /// + 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; + } + + /// + /// Write to data from an existing MSOleHeader + /// + 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 + } + + /// + /// MS-OLE Directory Entry (.msi) + /// + 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 + + /// 0x00 + public byte[] NAME { get; set; } + + /// + /// Length in bytes incl 0 terminator + /// + /// 0x40 + public ushort NAME_LEN { get; set; } + + /// 0x42 + public DIRENT_TYPE TYPE_FLAG { get; set; } + + /// 0x43 + public byte COLOR { get; set; } + + /// 0x44 + public uint PREV { get; set; } + + /// 0x48 + public uint NEXT { get; set; } + + /// 0x4C + public uint CHILD { get; set; } + + /// + /// Only for dirs + /// + /// 0x50 + public byte[] CLSID { get; set; } + + /// + /// Only for dirs + /// + /// 0x60 + public uint USERFLAGS { get; set; } + + /// + /// For files + /// + /// 0x64 + public ulong CREATE_TIME { get; set; } + + /// + /// For files + /// + /// 0x6C + public ulong MODIFY_TIME { get; set; } + + /// 0x74 + public uint FIRSTBLOCK { get; set; } + + /// 0x78 + public uint FILE_SIZE { get; set; } + + /// + /// 0x7c..0x7f reserved == 0 + /// + /// 0x7C + 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 + + /// + /// Private constructor + /// + private MSOleDirectoryEntry() { } + + /// + /// Create a new default MSOleDirectoryEntry + /// + public static MSOleDirectoryEntry CreateDefault() + { + // TODO: Figure out if there are any sane defaults + return new MSOleDirectoryEntry(); + } + + /// + /// Create a new MSOleDirectoryEntry from data + /// + 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; + } + + /// + /// Write to data from an existing MSOleHeader + /// + 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 } diff --git a/BurnOutSharp/External/libgsf/Input/GsfInfileMSOle.cs b/BurnOutSharp/External/libgsf/Input/GsfInfileMSOle.cs index dcf576f1..fd022322 100644 --- a/BurnOutSharp/External/libgsf/Input/GsfInfileMSOle.cs +++ b/BurnOutSharp/External/libgsf/Input/GsfInfileMSOle.cs @@ -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 Children { get; set; } + public List Children { get; set; } = new List(); /// /// 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 } /// - 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. /// /// A pointer to the element after the last position filled - 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; } /// @@ -538,8 +536,7 @@ namespace LibGSF.Input /// 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(), }; - // 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 /// True on error setting if it is supplied. 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(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) { diff --git a/BurnOutSharp/External/libgsf/Input/GsfInfileTar.cs b/BurnOutSharp/External/libgsf/Input/GsfInfileTar.cs index 8a7e8d71..c17e5dd2 100644 --- a/BurnOutSharp/External/libgsf/Input/GsfInfileTar.cs +++ b/BurnOutSharp/External/libgsf/Input/GsfInfileTar.cs @@ -199,7 +199,7 @@ namespace LibGSF.Input protected override byte[] ReadImpl(int num_bytes, byte[] optional_buffer, int bufferPtr = 0) => null; /// - public override bool Seek(long offset, SeekOrigin whence) => false; + protected override bool SeekImpl(long offset, SeekOrigin whence) => false; /// public override GsfInput ChildByIndex(int i, ref Exception error) diff --git a/BurnOutSharp/External/libgsf/Input/GsfInfileZip.cs b/BurnOutSharp/External/libgsf/Input/GsfInfileZip.cs index 541db3ea..babcc24c 100644 --- a/BurnOutSharp/External/libgsf/Input/GsfInfileZip.cs +++ b/BurnOutSharp/External/libgsf/Input/GsfInfileZip.cs @@ -269,7 +269,7 @@ namespace LibGSF.Input private static bool warned = false; /// - public override bool Seek(long offset, SeekOrigin whence) + protected override bool SeekImpl(long offset, SeekOrigin whence) { long pos = offset; diff --git a/BurnOutSharp/External/libgsf/Input/GsfInput.cs b/BurnOutSharp/External/libgsf/Input/GsfInput.cs index 9bd77e38..33ae65b7 100644 --- a/BurnOutSharp/External/libgsf/Input/GsfInput.cs +++ b/BurnOutSharp/External/libgsf/Input/GsfInput.cs @@ -176,7 +176,7 @@ namespace LibGSF.Input /// the end of the stream, or to the current location. /// /// True on error. - 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 } } diff --git a/BurnOutSharp/External/libgsf/Input/GsfInputGZip.cs b/BurnOutSharp/External/libgsf/Input/GsfInputGZip.cs index d692a94d..49bb6626 100644 --- a/BurnOutSharp/External/libgsf/Input/GsfInputGZip.cs +++ b/BurnOutSharp/External/libgsf/Input/GsfInputGZip.cs @@ -261,7 +261,7 @@ namespace LibGSF.Input private static bool warned = false; /// - public override bool Seek(long offset, SeekOrigin whence) + protected override bool SeekImpl(long offset, SeekOrigin whence) { long pos = offset; diff --git a/BurnOutSharp/External/libgsf/Input/GsfInputGio.cs b/BurnOutSharp/External/libgsf/Input/GsfInputGio.cs index a1f7cf65..7662045d 100644 --- a/BurnOutSharp/External/libgsf/Input/GsfInputGio.cs +++ b/BurnOutSharp/External/libgsf/Input/GsfInputGio.cs @@ -162,7 +162,7 @@ namespace LibGSF.Input } /// - public override bool Seek(long offset, SeekOrigin whence) + protected override bool SeekImpl(long offset, SeekOrigin whence) { if (Stream == null) return true; diff --git a/BurnOutSharp/External/libgsf/Input/GsfInputHTTP.cs b/BurnOutSharp/External/libgsf/Input/GsfInputHTTP.cs index 7b0cdc81..6255a27c 100644 --- a/BurnOutSharp/External/libgsf/Input/GsfInputHTTP.cs +++ b/BurnOutSharp/External/libgsf/Input/GsfInputHTTP.cs @@ -138,7 +138,7 @@ namespace LibGSF.Input } /// - public override bool Seek(long offset, SeekOrigin whence) => false; + protected override bool SeekImpl(long offset, SeekOrigin whence) => false; #endregion diff --git a/BurnOutSharp/External/libgsf/Input/GsfInputMemory.cs b/BurnOutSharp/External/libgsf/Input/GsfInputMemory.cs index f1df0456..6ec4bd20 100644 --- a/BurnOutSharp/External/libgsf/Input/GsfInputMemory.cs +++ b/BurnOutSharp/External/libgsf/Input/GsfInputMemory.cs @@ -96,7 +96,7 @@ namespace LibGSF.Input } /// - public override bool Seek(long offset, SeekOrigin whence) => false; + protected override bool SeekImpl(long offset, SeekOrigin whence) => false; #endregion } diff --git a/BurnOutSharp/External/libgsf/Input/GsfInputProxy.cs b/BurnOutSharp/External/libgsf/Input/GsfInputProxy.cs index 895a15dd..d6514a85 100644 --- a/BurnOutSharp/External/libgsf/Input/GsfInputProxy.cs +++ b/BurnOutSharp/External/libgsf/Input/GsfInputProxy.cs @@ -104,7 +104,7 @@ namespace LibGSF.Input } /// - public override bool Seek(long offset, SeekOrigin whence) => false; + protected override bool SeekImpl(long offset, SeekOrigin whence) => false; #endregion } diff --git a/BurnOutSharp/External/libgsf/Input/GsfInputStdio.cs b/BurnOutSharp/External/libgsf/Input/GsfInputStdio.cs index 4357c891..0a5bc586 100644 --- a/BurnOutSharp/External/libgsf/Input/GsfInputStdio.cs +++ b/BurnOutSharp/External/libgsf/Input/GsfInputStdio.cs @@ -206,7 +206,7 @@ namespace LibGSF.Input } /// - public override bool Seek(long offset, SeekOrigin whence) + protected override bool SeekImpl(long offset, SeekOrigin whence) { if (File == null) return true; diff --git a/BurnOutSharp/External/libgsf/Input/GsfInputTextline.cs b/BurnOutSharp/External/libgsf/Input/GsfInputTextline.cs index 4dae7867..ce1f01e5 100644 --- a/BurnOutSharp/External/libgsf/Input/GsfInputTextline.cs +++ b/BurnOutSharp/External/libgsf/Input/GsfInputTextline.cs @@ -85,7 +85,7 @@ namespace LibGSF.Input } /// - public override bool Seek(long offset, SeekOrigin whence) + protected override bool SeekImpl(long offset, SeekOrigin whence) { Remainder = null; bool res = Source.Seek(offset, whence); diff --git a/BurnOutSharp/External/libgsf/Input/GsfStructuredBlob.cs b/BurnOutSharp/External/libgsf/Input/GsfStructuredBlob.cs index 7997f559..3193fb9c 100644 --- a/BurnOutSharp/External/libgsf/Input/GsfStructuredBlob.cs +++ b/BurnOutSharp/External/libgsf/Input/GsfStructuredBlob.cs @@ -90,7 +90,7 @@ namespace LibGSF.Input } /// - public override bool Seek(long offset, SeekOrigin whence) => false; + protected override bool SeekImpl(long offset, SeekOrigin whence) => false; /// public override int NumChildren() => Children != null ? Children.Length : -1; diff --git a/BurnOutSharp/External/libgsf/Output/GsfOutfileMSOle.cs b/BurnOutSharp/External/libgsf/Output/GsfOutfileMSOle.cs index d2dea292..efaab52e 100644 --- a/BurnOutSharp/External/libgsf/Output/GsfOutfileMSOle.cs +++ b/BurnOutSharp/External/libgsf/Output/GsfOutfileMSOle.cs @@ -192,19 +192,20 @@ namespace LibGSF.Output ole.RegisterChild(ole); // Build the header - byte[] buf = Enumerable.Repeat(0xFF, OLE_HEADER_SIZE).ToArray(); - Array.Copy(default_header, buf, default_header.Length); + byte[] buf = Enumerable.Repeat(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. /// - 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 /// 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(0, DIRENT_SIZE).ToArray(); + buf = Enumerable.Repeat(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) { diff --git a/BurnOutSharp/External/libmsi/LibmsiDatabase.cs b/BurnOutSharp/External/libmsi/LibmsiDatabase.cs index 2dbaf17e..36e00d47 100644 --- a/BurnOutSharp/External/libmsi/LibmsiDatabase.cs +++ b/BurnOutSharp/External/libmsi/LibmsiDatabase.cs @@ -157,13 +157,13 @@ namespace LibMSI internal int MediaTransformDiskId { get; set; } - internal LinkedList Tables { get; set; } + internal LinkedList Tables { get; set; } = new LinkedList(); - internal LinkedList Transforms { get; set; } + internal LinkedList Transforms { get; set; } = new LinkedList(); - internal LinkedList Streams { get; set; } + internal LinkedList Streams { get; set; } = new LinkedList(); - internal LinkedList Storages { get; set; } + internal LinkedList Storages { get; set; } = new LinkedList(); #endregion