Seek -> SeekImpl

This commit is contained in:
Matt Nadareski
2022-06-17 13:41:51 -07:00
parent 1860a863b8
commit 4d5d2d8690
15 changed files with 650 additions and 229 deletions

View File

@@ -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
}

View File

@@ -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)
{

View File

@@ -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)

View File

@@ -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;

View File

@@ -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
}
}

View File

@@ -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;

View File

@@ -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;

View File

@@ -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

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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;

View File

@@ -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);

View File

@@ -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;

View File

@@ -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)
{

View File

@@ -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