mirror of
https://github.com/SabreTools/BinaryObjectScanner.git
synced 2026-07-09 02:16:46 +00:00
Cleanup and minor MS-OLE fixes
This commit is contained in:
43
BurnOutSharp/External/libgsf/GsfMSOleImpl.cs
vendored
43
BurnOutSharp/External/libgsf/GsfMSOleImpl.cs
vendored
@@ -163,6 +163,9 @@ namespace LibGSF
|
||||
/// <remarks>0x34</remarks>
|
||||
public uint TRANSACTING_SIGNATURE { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Transition between small and big blocks
|
||||
/// </summary>
|
||||
/// <remarks>0x38</remarks>
|
||||
public uint THRESHOLD { get; set; }
|
||||
|
||||
@@ -185,6 +188,11 @@ namespace LibGSF
|
||||
|
||||
#region Derived Properties
|
||||
|
||||
/// <summary>
|
||||
/// Indicate if the contents are Intel Little-Endian
|
||||
/// </summary>
|
||||
public bool LITTLE_ENDIAN => BYTE_ORDER == 0xFFFE;
|
||||
|
||||
public int BB_SIZE => 1 << BB_SHIFT;
|
||||
|
||||
public int BB_FILTER => BB_SIZE << 1;
|
||||
@@ -254,6 +262,15 @@ namespace LibGSF
|
||||
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);
|
||||
|
||||
// It makes no sense to have a block larger than 2^31 for now.
|
||||
// Maybe relax this later, but not much.
|
||||
if (6 > header.BB_SHIFT || header.BB_SHIFT >= 31 || header.SB_SHIFT > header.BB_SHIFT)
|
||||
{
|
||||
err = new ArgumentException("Unreasonable block sizes");
|
||||
return null;
|
||||
}
|
||||
|
||||
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);
|
||||
@@ -274,6 +291,13 @@ namespace LibGSF
|
||||
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);
|
||||
|
||||
if (header.NUM_SBAT == 0
|
||||
&& header.SBAT_START != GsfMSOleImpl.BAT_MAGIC_END_OF_CHAIN
|
||||
&& header.SBAT_START != GsfMSOleImpl.BAT_MAGIC_UNUSED)
|
||||
{
|
||||
Console.Error.WriteLine("There are not supposed to be any blocks in the small block allocation table, yet there is a link to some. Ignoring it.");
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
@@ -385,7 +409,7 @@ namespace LibGSF
|
||||
public uint CHILD { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Only for dirs
|
||||
/// 16 byte GUID used by some apps; Only for dirs
|
||||
/// </summary>
|
||||
/// <remarks>0x50</remarks>
|
||||
public byte[] CLSID { get; set; }
|
||||
@@ -424,12 +448,15 @@ namespace LibGSF
|
||||
|
||||
#region Derived Properties
|
||||
|
||||
/// <summary>
|
||||
/// Directory Entry name as a UTF-8 encoded string
|
||||
/// </summary>
|
||||
public string NAME_STRING
|
||||
{
|
||||
get
|
||||
{
|
||||
if (NAME_LEN <= 0 || NAME_LEN > DIRENT_MAX_NAME_SIZE)
|
||||
return null;
|
||||
return string.Empty;
|
||||
|
||||
// !#%!@$#^
|
||||
// Sometimes, rarely, people store the stream name as ascii
|
||||
@@ -441,16 +468,24 @@ namespace LibGSF
|
||||
if (end == -1)
|
||||
{
|
||||
byte[] direntNameBytes = Encoding.Convert(Encoding.ASCII, Encoding.UTF8, NAME);
|
||||
return Encoding.UTF8.GetString(direntNameBytes);
|
||||
return Encoding.UTF8.GetString(direntNameBytes).TrimEnd('\0');
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] direntNameBytes = Encoding.Convert(Encoding.Unicode, Encoding.UTF8, NAME);
|
||||
return Encoding.UTF8.GetString(direntNameBytes);
|
||||
return Encoding.UTF8.GetString(direntNameBytes).TrimEnd('\0');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Last modified time as a nullable DateTime
|
||||
/// </summary>
|
||||
public DateTime? MODIFY_DATETIME => MODIFY_TIME == 0 ? (DateTime?)null : DateTime.FromFileTime((long)MODIFY_TIME);
|
||||
|
||||
/// <summary>
|
||||
/// Determine if this entry is a directory
|
||||
/// </summary>
|
||||
public bool IS_DIRECTORY => TYPE_FLAG != DIRENT_TYPE.DIRENT_TYPE_FILE;
|
||||
|
||||
#endregion
|
||||
|
||||
289
BurnOutSharp/External/libgsf/Input/GsfInfileMSOle.cs
vendored
289
BurnOutSharp/External/libgsf/Input/GsfInfileMSOle.cs
vendored
@@ -37,9 +37,16 @@ namespace LibGSF.Input
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public uint[] Block { get; set; } = null;
|
||||
public uint[] Blocks { get; set; } = null;
|
||||
|
||||
public uint NumBlocks { get; set; }
|
||||
#endregion
|
||||
|
||||
#region Derived Properties
|
||||
|
||||
/// <summary>
|
||||
/// Number of blocks in the BAT as an unsigned Int32
|
||||
/// </summary>
|
||||
public uint NumBlocks => (uint)(Blocks?.Length ?? 0);
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -55,14 +62,13 @@ namespace LibGSF.Input
|
||||
/// table for the list starting in <paramref name="block"/>.
|
||||
/// </summary>
|
||||
/// <param name="metabat">A meta bat to connect to the raw blocks (small or large)</param>
|
||||
/// <param name="size_guess">An optional guess as to how many blocks are in the file</param>
|
||||
/// <param name="block">The first block in the list.</param>
|
||||
/// <param name="res">Where to store the result.</param>
|
||||
/// <returns>True on error.</returns>
|
||||
public static bool Create(MSOleBAT metabat, int size_guess, uint block, out MSOleBAT res)
|
||||
public static bool Create(MSOleBAT metabat, uint block, out MSOleBAT res)
|
||||
{
|
||||
// NOTE : Only use size as a suggestion, sometimes it is wrong
|
||||
List<uint> bat = new List<uint>(size_guess);
|
||||
List<uint> bat = new List<uint>();
|
||||
byte[] used = new byte[1 + metabat.NumBlocks / 8];
|
||||
|
||||
while (block < metabat.NumBlocks)
|
||||
@@ -74,14 +80,10 @@ namespace LibGSF.Input
|
||||
used[block / 8] |= (byte)(1 << (int)(block & 0x7));
|
||||
|
||||
bat.Add(block);
|
||||
block = metabat.Block[block];
|
||||
block = metabat.Blocks[block];
|
||||
}
|
||||
|
||||
res = new MSOleBAT
|
||||
{
|
||||
NumBlocks = (uint)bat.Count,
|
||||
Block = bat.ToArray(),
|
||||
};
|
||||
res = new MSOleBAT { Blocks = bat.ToArray() };
|
||||
|
||||
if (block != BAT_MAGIC_END_OF_CHAIN)
|
||||
{
|
||||
@@ -94,19 +96,6 @@ namespace LibGSF.Input
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
|
||||
public void Release()
|
||||
{
|
||||
if (Block == null)
|
||||
return;
|
||||
|
||||
NumBlocks = 0;
|
||||
Block = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
// TODO: Can this be made internal?
|
||||
@@ -114,28 +103,18 @@ namespace LibGSF.Input
|
||||
{
|
||||
#region Properties
|
||||
|
||||
public string Name { get; set; }
|
||||
|
||||
public GsfMSOleSortingKey Key { get; set; }
|
||||
|
||||
public int Index { get; set; }
|
||||
|
||||
public int Size { get; set; }
|
||||
public uint Index { get; set; }
|
||||
|
||||
public bool UseSmallBlock { get; set; }
|
||||
|
||||
public uint FirstBlock { get; set; }
|
||||
|
||||
public bool IsDirectory { get; set; }
|
||||
|
||||
public List<MSOleDirent> Children { get; set; } = new List<MSOleDirent>();
|
||||
|
||||
/// <summary>
|
||||
/// 16 byte GUID used by some apps
|
||||
/// Internal representation of the MS-OLE directory entry header
|
||||
/// </summary>
|
||||
public byte[] ClassID { get; set; } = new byte[16];
|
||||
|
||||
public DateTime? ModTime { get; set; }
|
||||
internal MSOleDirectoryEntry Header { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -143,7 +122,6 @@ namespace LibGSF.Input
|
||||
|
||||
public void Free()
|
||||
{
|
||||
Name = null;
|
||||
Key = null;
|
||||
|
||||
foreach (MSOleDirent child in Children)
|
||||
@@ -152,7 +130,7 @@ namespace LibGSF.Input
|
||||
}
|
||||
|
||||
Children = null;
|
||||
ModTime = null;
|
||||
Header = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
@@ -161,37 +139,16 @@ namespace LibGSF.Input
|
||||
// TODO: Can this be made internal?
|
||||
public class MSOleInfo
|
||||
{
|
||||
#region Classes
|
||||
|
||||
public class MSOleInfoBlock
|
||||
{
|
||||
public MSOleBAT Bat { get; set; }
|
||||
|
||||
public int Shift { get; set; }
|
||||
|
||||
public int Filter { get; set; }
|
||||
|
||||
public int Size { get; set; }
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
||||
public MSOleInfoBlock BigBlock { get; set; }
|
||||
public MSOleBAT BigBlockBat { get; set; }
|
||||
|
||||
public MSOleInfoBlock SmallBlock { get; set; }
|
||||
|
||||
public long MaxBlock { get; set; }
|
||||
public MSOleBAT SmallBlockBat { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Transition between small and big blocks
|
||||
/// Maximum number of blocks derived from total input length and block size
|
||||
/// </summary>
|
||||
public uint Threshold { get; set; }
|
||||
|
||||
public uint SBatStart { get; set; }
|
||||
|
||||
public uint NumSbat { get; set; }
|
||||
public long MaxBlock { get; set; }
|
||||
|
||||
public MSOleDirent RootDir { get; set; }
|
||||
|
||||
@@ -199,6 +156,11 @@ namespace LibGSF.Input
|
||||
|
||||
public int RefCount { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Internal representation of the MS-OLE header
|
||||
/// </summary>
|
||||
internal MSOleHeader Header { get; set; }
|
||||
|
||||
#endregion
|
||||
|
||||
#region Functions
|
||||
@@ -208,8 +170,6 @@ namespace LibGSF.Input
|
||||
if (RefCount-- != 1)
|
||||
return;
|
||||
|
||||
BigBlock.Bat.Release();
|
||||
SmallBlock.Bat.Release();
|
||||
if (RootDir != null)
|
||||
{
|
||||
RootDir.Free();
|
||||
@@ -218,6 +178,8 @@ namespace LibGSF.Input
|
||||
|
||||
if (SmallBlockFile != null)
|
||||
SmallBlockFile = null;
|
||||
|
||||
Header = null;
|
||||
}
|
||||
|
||||
public MSOleInfo Ref()
|
||||
@@ -298,7 +260,6 @@ namespace LibGSF.Input
|
||||
Info = null;
|
||||
}
|
||||
|
||||
Bat.Release();
|
||||
Stream = null;
|
||||
}
|
||||
|
||||
@@ -327,25 +288,22 @@ namespace LibGSF.Input
|
||||
}
|
||||
|
||||
// GsfInput guarantees that num_bytes > 0
|
||||
long first_block = (CurrentOffset >> Info.BigBlock.Shift);
|
||||
long last_block = ((CurrentOffset + num_bytes - 1) >> Info.BigBlock.Shift);
|
||||
long offset = CurrentOffset & Info.BigBlock.Filter;
|
||||
long first_block = (CurrentOffset >> Info.Header.BB_SHIFT);
|
||||
long last_block = ((CurrentOffset + num_bytes - 1) >> Info.Header.BB_SHIFT);
|
||||
long offset = CurrentOffset & Info.Header.BB_FILTER;
|
||||
|
||||
if (last_block >= Bat.NumBlocks)
|
||||
return null;
|
||||
|
||||
// Optimization: are all the raw blocks contiguous?
|
||||
long i = first_block;
|
||||
long raw_block = Bat.Block[i];
|
||||
long raw_block = Bat.Blocks[i];
|
||||
|
||||
//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.Blocks[i]) ;
|
||||
|
||||
if (i > last_block)
|
||||
{
|
||||
if (!SeekBlock(Bat.Block[first_block], offset))
|
||||
if (!SeekBlock(Bat.Blocks[first_block], offset))
|
||||
return null;
|
||||
|
||||
CurBlock = last_block;
|
||||
@@ -366,11 +324,11 @@ namespace LibGSF.Input
|
||||
int count;
|
||||
for (i = first_block; i <= last_block; i++, ptr += count, num_bytes -= count)
|
||||
{
|
||||
count = (int)(Info.BigBlock.Size - offset);
|
||||
count = (int)(Info.Header.BB_SIZE - offset);
|
||||
if (count > num_bytes)
|
||||
count = num_bytes;
|
||||
|
||||
if (!SeekBlock(Bat.Block[i], offset))
|
||||
if (!SeekBlock(Bat.Blocks[i], offset))
|
||||
return null;
|
||||
|
||||
if (Input.Read(count, optional_buffer, ptr) == null)
|
||||
@@ -409,7 +367,7 @@ namespace LibGSF.Input
|
||||
foreach (MSOleDirent dirent in DirectoryEntry.Children)
|
||||
{
|
||||
if (i-- <= 0)
|
||||
return dirent.Name;
|
||||
return dirent.Header.NAME_STRING;
|
||||
}
|
||||
|
||||
return null;
|
||||
@@ -420,7 +378,7 @@ namespace LibGSF.Input
|
||||
{
|
||||
foreach (MSOleDirent dirent in DirectoryEntry.Children)
|
||||
{
|
||||
if (dirent.Name != null && dirent.Name == name)
|
||||
if (dirent.Header.NAME_STRING != null && dirent.Header.NAME_STRING == name)
|
||||
return CreateChild(dirent, ref error);
|
||||
}
|
||||
|
||||
@@ -433,7 +391,7 @@ namespace LibGSF.Input
|
||||
if (DirectoryEntry == null)
|
||||
return -1;
|
||||
|
||||
if (!DirectoryEntry.IsDirectory)
|
||||
if (!DirectoryEntry.Header.IS_DIRECTORY)
|
||||
return -1;
|
||||
|
||||
return DirectoryEntry.Children.Count;
|
||||
@@ -450,7 +408,7 @@ namespace LibGSF.Input
|
||||
if (DirectoryEntry == null)
|
||||
return false;
|
||||
|
||||
Array.Copy(DirectoryEntry.ClassID, res, DirectoryEntry.ClassID.Length);
|
||||
Array.Copy(DirectoryEntry.Header.CLSID, res, DirectoryEntry.Header.CLSID.Length);
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -465,8 +423,8 @@ namespace LibGSF.Input
|
||||
return false;
|
||||
|
||||
// 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(MSOleHeader.OLE_HEADER_SIZE, Info.BigBlock.Size) + (block << Info.BigBlock.Shift) + offset, SeekOrigin.Begin))
|
||||
// header is padded out to BB_SIZE (sector size) when BB_SIZE > 512.
|
||||
if (Input.Seek(Math.Max(MSOleHeader.OLE_HEADER_SIZE, Info.Header.BB_SIZE) + (block << Info.Header.BB_SHIFT) + offset, SeekOrigin.Begin))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
@@ -484,7 +442,7 @@ namespace LibGSF.Input
|
||||
if (!SeekBlock(block, 0))
|
||||
return null;
|
||||
|
||||
return Input.Read(Info.BigBlock.Size, buffer);
|
||||
return Input.Read(Info.Header.BB_SIZE, buffer);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -502,7 +460,7 @@ namespace LibGSF.Input
|
||||
if (bat == null)
|
||||
return null;
|
||||
|
||||
int end = Info.BigBlock.Size;
|
||||
int end = Info.Header.BB_SIZE;
|
||||
for (int batPtr = 0; batPtr < end; batPtr += BAT_INDEX_SIZE, batsPtr++)
|
||||
{
|
||||
bats[batsPtr] = GSF_LE_GET_GUINT32(bat, batPtr);
|
||||
@@ -519,7 +477,7 @@ namespace LibGSF.Input
|
||||
// 'unused' entries in the metabat. Let's assume that
|
||||
// corresponds to lots of unused blocks
|
||||
// http://bugzilla.gnome.org/show_bug.cgi?id=336858
|
||||
uint i = (uint)(Info.BigBlock.Size / BAT_INDEX_SIZE);
|
||||
uint i = (uint)(Info.Header.BB_SIZE / BAT_INDEX_SIZE);
|
||||
while (i-- > 0)
|
||||
{
|
||||
bats[batsPtr++] = BAT_MAGIC_UNUSED;
|
||||
@@ -533,9 +491,9 @@ namespace LibGSF.Input
|
||||
/// <summary>
|
||||
/// Copy some some raw data into an array of uint.
|
||||
/// </summary>
|
||||
private static void GetUnsignedInts(uint[] dst, byte[] src, int srcPtr, int num_bytes)
|
||||
private static void GetUnsignedInts(uint[] dst, ref int dstPtr, byte[] src, int srcPtr, int num_bytes)
|
||||
{
|
||||
for (int dstPtr = 0; (num_bytes -= BAT_INDEX_SIZE) >= 0; srcPtr += BAT_INDEX_SIZE)
|
||||
for (; (num_bytes -= BAT_INDEX_SIZE) >= 0; srcPtr += BAT_INDEX_SIZE)
|
||||
{
|
||||
dst[dstPtr++] = GSF_LE_GET_GUINT32(src, srcPtr);
|
||||
}
|
||||
@@ -555,16 +513,14 @@ namespace LibGSF.Input
|
||||
if (Info.SmallBlockFile is GsfInfileMSOle)
|
||||
(Info.SmallBlockFile as GsfInfileMSOle).Info.Unref();
|
||||
|
||||
if (Info.SmallBlock.Bat.Block != null)
|
||||
if (Info.SmallBlockBat.Blocks != null)
|
||||
return null;
|
||||
|
||||
if (MSOleBAT.Create(Info.BigBlock.Bat, (int)Info.NumSbat, Info.SBatStart, out MSOleBAT meta_sbat))
|
||||
if (MSOleBAT.Create(Info.BigBlockBat, Info.Header.SBAT_START, out MSOleBAT meta_sbat))
|
||||
return null;
|
||||
|
||||
Info.SmallBlock.Bat.NumBlocks = (uint)(meta_sbat.NumBlocks * (Info.BigBlock.Size / BAT_INDEX_SIZE));
|
||||
Info.SmallBlock.Bat.Block = new uint[Info.SmallBlock.Bat.NumBlocks];
|
||||
ReadMetabat(Info.SmallBlock.Bat.Block, 0, Info.SmallBlock.Bat.NumBlocks, meta_sbat.Block, meta_sbat.NumBlocks);
|
||||
meta_sbat.Release();
|
||||
Info.SmallBlockBat.Blocks = new uint[meta_sbat.NumBlocks * (Info.Header.BB_SIZE / BAT_INDEX_SIZE)];
|
||||
ReadMetabat(Info.SmallBlockBat.Blocks, 0, Info.SmallBlockBat.NumBlocks, meta_sbat.Blocks, meta_sbat.NumBlocks);
|
||||
|
||||
return Info.SmallBlockFile;
|
||||
}
|
||||
@@ -584,7 +540,7 @@ namespace LibGSF.Input
|
||||
if (entry > uint.MaxValue / MSOleDirectoryEntry.DIRENT_SIZE)
|
||||
return null;
|
||||
|
||||
uint block = ((entry * MSOleDirectoryEntry.DIRENT_SIZE) >> Info.BigBlock.Shift);
|
||||
uint block = ((entry * MSOleDirectoryEntry.DIRENT_SIZE) >> Info.Header.BB_SHIFT);
|
||||
if (block >= Bat.NumBlocks)
|
||||
return null;
|
||||
|
||||
@@ -593,12 +549,12 @@ namespace LibGSF.Input
|
||||
|
||||
seen_before[entry] = true;
|
||||
|
||||
byte[] data = GetBlock(Bat.Block[block], null);
|
||||
byte[] data = GetBlock(Bat.Blocks[block], null);
|
||||
if (data == null)
|
||||
return null;
|
||||
|
||||
int dataPtr = 0; // data[0]
|
||||
dataPtr += (int)((MSOleDirectoryEntry.DIRENT_SIZE * entry) % Info.BigBlock.Size);
|
||||
dataPtr += (int)((MSOleDirectoryEntry.DIRENT_SIZE * entry) % Info.Header.BB_SIZE);
|
||||
|
||||
Exception err = null;
|
||||
MSOleDirectoryEntry directoryEntry = MSOleDirectoryEntry.Create(data, dataPtr, ref err);
|
||||
@@ -621,28 +577,16 @@ namespace LibGSF.Input
|
||||
|
||||
MSOleDirent dirent = new MSOleDirent
|
||||
{
|
||||
Index = (int)entry,
|
||||
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,
|
||||
Key = GsfMSOleSortingKey.Create(directoryEntry.NAME_STRING),
|
||||
Index = entry,
|
||||
|
||||
// Root dir is always big block
|
||||
UseSmallBlock = parent != null && (directoryEntry.FILE_SIZE < Info.Threshold),
|
||||
FirstBlock = directoryEntry.FIRSTBLOCK,
|
||||
IsDirectory = directoryEntry.IS_DIRECTORY,
|
||||
UseSmallBlock = parent != null && (directoryEntry.FILE_SIZE < Info.Header.THRESHOLD),
|
||||
Children = new List<MSOleDirent>(),
|
||||
|
||||
Header = directoryEntry,
|
||||
};
|
||||
|
||||
dirent.Name = directoryEntry.NAME_STRING.TrimEnd('\0');
|
||||
|
||||
// Be really anal in the face of screwups
|
||||
if (dirent.Name == null)
|
||||
dirent.Name = string.Empty;
|
||||
|
||||
dirent.Key = GsfMSOleSortingKey.Create(dirent.Name);
|
||||
|
||||
if (parent != null)
|
||||
{
|
||||
parent.Children.Add(dirent);
|
||||
@@ -653,7 +597,7 @@ namespace LibGSF.Input
|
||||
CreateDirectoryEntry(directoryEntry.PREV, parent, seen_before);
|
||||
CreateDirectoryEntry(directoryEntry.NEXT, parent, seen_before);
|
||||
|
||||
if (dirent.IsDirectory)
|
||||
if (dirent.Header.IS_DIRECTORY)
|
||||
CreateDirectoryEntry(directoryEntry.CHILD, dirent, seen_before);
|
||||
else if (directoryEntry.CHILD != MSOleDirectoryEntry.DIRENT_MAGIC_END)
|
||||
Console.Error.WriteLine("A non directory stream with children ?");
|
||||
@@ -707,72 +651,59 @@ namespace LibGSF.Input
|
||||
|
||||
MSOleHeader headerImpl = MSOleHeader.Create(header, 0, ref err);
|
||||
if (headerImpl == null)
|
||||
{
|
||||
err = new Exception("Header could not be parsed");
|
||||
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 > headerImpl.BB_SHIFT || headerImpl.BB_SHIFT >= 31 || headerImpl.SB_SHIFT > headerImpl.BB_SHIFT || (Input.Size >> headerImpl.BB_SHIFT) < 1)
|
||||
// There should always be at least 1 BAT block
|
||||
if ((Input.Size >> headerImpl.BB_SHIFT) < 1)
|
||||
{
|
||||
err = new Exception("Unreasonable block sizes");
|
||||
return true;
|
||||
}
|
||||
|
||||
MSOleInfo info = new MSOleInfo
|
||||
Info = new MSOleInfo
|
||||
{
|
||||
BigBlock = new MSOleInfo.MSOleInfoBlock { Bat = new MSOleBAT() },
|
||||
SmallBlock = new MSOleInfo.MSOleInfoBlock { Bat = new MSOleBAT() },
|
||||
BigBlockBat = new MSOleBAT(),
|
||||
SmallBlockBat = new MSOleBAT(),
|
||||
MaxBlock = (Input.Size - MSOleHeader.OLE_HEADER_SIZE + headerImpl.BB_SIZE - 1) / headerImpl.BB_SIZE,
|
||||
RootDir = null,
|
||||
SmallBlockFile = null,
|
||||
RefCount = 1,
|
||||
|
||||
Header = headerImpl,
|
||||
};
|
||||
|
||||
info.RefCount = 1;
|
||||
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;
|
||||
|
||||
if (info.NumSbat == 0 && info.SBatStart != BAT_MAGIC_END_OF_CHAIN && info.SBatStart != BAT_MAGIC_UNUSED)
|
||||
Console.Error.WriteLine("There are not supposed to be any blocks in the small block allocation table, yet there is a link to some. Ignoring it.");
|
||||
|
||||
uint[] metabat = null;
|
||||
int metabatPtr = 0; // metabat[0]
|
||||
uint last;
|
||||
int? ptr = null;
|
||||
|
||||
// Very rough heuristic, just in case
|
||||
uint num_bat = headerImpl.NUM_BAT;
|
||||
if (num_bat < info.MaxBlock && headerImpl.NUM_SBAT < info.MaxBlock)
|
||||
if (num_bat < Info.MaxBlock && headerImpl.NUM_SBAT < Info.MaxBlock)
|
||||
{
|
||||
info.BigBlock.Bat.NumBlocks = (uint)(num_bat * (headerImpl.BB_SIZE / BAT_INDEX_SIZE));
|
||||
info.BigBlock.Bat.Block = new uint[info.BigBlock.Bat.NumBlocks];
|
||||
Info.BigBlockBat.Blocks = new uint[num_bat * (headerImpl.BB_SIZE / BAT_INDEX_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, MSOleHeader.OLE_HEADER_SIZE - OLE_HEADER_START_BAT);
|
||||
last = num_bat;
|
||||
if (last > OLE_HEADER_METABAT_SIZE)
|
||||
last = OLE_HEADER_METABAT_SIZE;
|
||||
|
||||
ptr = ReadMetabat(info.BigBlock.Bat.Block, 0, info.BigBlock.Bat.NumBlocks, metabat, last);
|
||||
GetUnsignedInts(metabat, ref metabatPtr, header, OLE_HEADER_START_BAT, MSOleHeader.OLE_HEADER_SIZE - OLE_HEADER_START_BAT);
|
||||
|
||||
last = Math.Min(num_bat, OLE_HEADER_METABAT_SIZE);
|
||||
ptr = ReadMetabat(Info.BigBlockBat.Blocks, 0, Info.BigBlockBat.NumBlocks, metabat, last); // TODO: Does this need to be offset by metabatPtr?
|
||||
num_bat -= last;
|
||||
}
|
||||
|
||||
uint metabat_block = headerImpl.METABAT_BLOCK;
|
||||
uint num_metabat = headerImpl.NUM_METABAT;
|
||||
|
||||
last = (uint)((info.BigBlock.Size - BAT_INDEX_SIZE) / BAT_INDEX_SIZE);
|
||||
last = (uint)((Info.Header.BB_SIZE - BAT_INDEX_SIZE) / BAT_INDEX_SIZE);
|
||||
while (ptr != null && num_metabat-- > 0)
|
||||
{
|
||||
byte[] tmp = GetBlock(metabat_block, null);
|
||||
byte[] tmp = GetBlock(metabat_block - 1, null);
|
||||
if (tmp == null)
|
||||
{
|
||||
ptr = null;
|
||||
@@ -780,7 +711,7 @@ namespace LibGSF.Input
|
||||
}
|
||||
|
||||
// Reading the elements invalidates this memory, make copy
|
||||
GetUnsignedInts(metabat, tmp, 0, info.BigBlock.Size);
|
||||
GetUnsignedInts(metabat, ref metabatPtr, tmp, 0, Info.Header.BB_SIZE);
|
||||
|
||||
if (num_metabat == 0)
|
||||
{
|
||||
@@ -808,7 +739,7 @@ namespace LibGSF.Input
|
||||
num_bat -= last;
|
||||
}
|
||||
|
||||
ptr = ReadMetabat(info.BigBlock.Bat.Block, ptr.Value, info.BigBlock.Bat.NumBlocks, metabat, last);
|
||||
ptr = ReadMetabat(Info.BigBlockBat.Blocks, ptr.Value, Info.BigBlockBat.NumBlocks, metabat, last);
|
||||
}
|
||||
|
||||
bool fail = (ptr == null);
|
||||
@@ -820,7 +751,7 @@ namespace LibGSF.Input
|
||||
}
|
||||
|
||||
// Read the directory's bat, we do not know the size
|
||||
if (MSOleBAT.Create(Info.BigBlock.Bat, 0, headerImpl.DIRENT_START, out MSOleBAT tempBat))
|
||||
if (MSOleBAT.Create(Info.BigBlockBat, headerImpl.DIRENT_START, out MSOleBAT tempBat))
|
||||
{
|
||||
err = new Exception("Problems making block allocation table");
|
||||
return true;
|
||||
@@ -829,8 +760,8 @@ namespace LibGSF.Input
|
||||
Bat = tempBat;
|
||||
|
||||
// Read the directory
|
||||
bool[] seen_before = new bool[(Bat.NumBlocks << info.BigBlock.Shift) * MSOleDirectoryEntry.DIRENT_SIZE + 1];
|
||||
DirectoryEntry = info.RootDir = CreateDirectoryEntry(0, null, seen_before);
|
||||
bool[] seen_before = new bool[(Bat.NumBlocks << Info.Header.BB_SHIFT) * MSOleDirectoryEntry.DIRENT_SIZE + 1];
|
||||
DirectoryEntry = Info.RootDir = CreateDirectoryEntry(0, null, seen_before);
|
||||
if (DirectoryEntry == null)
|
||||
{
|
||||
err = new Exception("Problems reading directory");
|
||||
@@ -839,7 +770,7 @@ namespace LibGSF.Input
|
||||
|
||||
// The spec says to ignore modtime for root object. That doesn't
|
||||
// keep files from actually have a modtime there.
|
||||
ModTime = DirectoryEntry.ModTime;
|
||||
ModTime = DirectoryEntry.Header.MODIFY_DATETIME;
|
||||
|
||||
return false;
|
||||
}
|
||||
@@ -865,16 +796,16 @@ namespace LibGSF.Input
|
||||
return null;
|
||||
|
||||
child.DirectoryEntry = dirent;
|
||||
child.Size = dirent.Size;
|
||||
child.ModTime = dirent.ModTime;
|
||||
child.Size = dirent.Header.FILE_SIZE;
|
||||
child.ModTime = dirent.Header.MODIFY_DATETIME;
|
||||
|
||||
// The root dirent defines the small block file
|
||||
if (dirent.Index != 0)
|
||||
{
|
||||
child.Name = dirent.Name;
|
||||
child.Name = dirent.Header.NAME_STRING;
|
||||
child.Container = this;
|
||||
|
||||
if (dirent.IsDirectory)
|
||||
if (dirent.Header.IS_DIRECTORY)
|
||||
{
|
||||
// Be wary. It seems as if some implementations pretend that the
|
||||
// directories contain data
|
||||
@@ -883,17 +814,13 @@ namespace LibGSF.Input
|
||||
}
|
||||
}
|
||||
|
||||
MSOleInfo info = Info;
|
||||
|
||||
MSOleBAT metabat;
|
||||
int size_guess;
|
||||
GsfInput sb_file = null;
|
||||
|
||||
// Build the bat
|
||||
if (dirent.UseSmallBlock)
|
||||
{
|
||||
metabat = info.SmallBlock.Bat;
|
||||
size_guess = dirent.Size >> info.SmallBlock.Shift;
|
||||
metabat = Info.SmallBlockBat;
|
||||
sb_file = GetSmallBlockFile();
|
||||
if (sb_file == null)
|
||||
{
|
||||
@@ -903,11 +830,10 @@ namespace LibGSF.Input
|
||||
}
|
||||
else
|
||||
{
|
||||
metabat = info.BigBlock.Bat;
|
||||
size_guess = dirent.Size >> info.BigBlock.Shift;
|
||||
metabat = Info.BigBlockBat;
|
||||
}
|
||||
|
||||
if (MSOleBAT.Create(metabat, size_guess + 1, dirent.FirstBlock, out MSOleBAT tempBat))
|
||||
if (MSOleBAT.Create(metabat, dirent.Header.FIRSTBLOCK, out MSOleBAT tempBat))
|
||||
return null;
|
||||
|
||||
child.Bat = tempBat;
|
||||
@@ -917,26 +843,27 @@ namespace LibGSF.Input
|
||||
if (sb_file == null)
|
||||
return null;
|
||||
|
||||
int remaining = dirent.Size;
|
||||
uint remaining = dirent.Header.FILE_SIZE;
|
||||
child.Stream = new byte[remaining];
|
||||
|
||||
for (uint i = 0; remaining > 0 && i < child.Bat.NumBlocks; i++, remaining -= info.SmallBlock.Size)
|
||||
for (uint i = 0; remaining > 0 && i < child.Bat.NumBlocks; i++, remaining -= (uint)Info.Header.SB_SIZE)
|
||||
{
|
||||
if (sb_file.Seek(child.Bat.Block[i] << info.SmallBlock.Shift, SeekOrigin.Begin)
|
||||
|| sb_file.Read(Math.Min(remaining, info.SmallBlock.Size), child.Stream, (int)(i << info.SmallBlock.Shift)) == null)
|
||||
if (sb_file.Seek(child.Bat.Blocks[i] << Info.Header.SB_SHIFT, SeekOrigin.Begin)
|
||||
|| sb_file.Read((int)Math.Min(remaining, Info.Header.SB_SIZE), child.Stream, (int)(i << Info.Header.SB_SHIFT)) == null)
|
||||
{
|
||||
Console.Error.WriteLine($"Failure reading block {i} for '{dirent.Name}'");
|
||||
Console.Error.WriteLine($"Failure reading block {i} for '{dirent.Header.NAME_STRING}'");
|
||||
err = new Exception("Failure reading block");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
if (remaining > 0)
|
||||
{
|
||||
err = new Exception("Insufficient blocks");
|
||||
Console.Error.WriteLine($"Small-block file '{dirent.Name}' has insufficient blocks ({child.Bat.NumBlocks}) for the stated size ({dirent.Size})");
|
||||
return null;
|
||||
}
|
||||
// TODO: Debug as to why this block would be hit
|
||||
//if (remaining > 0)
|
||||
//{
|
||||
// err = new Exception("Insufficient blocks");
|
||||
// Console.Error.WriteLine($"Small-block file '{dirent.Header.NAME_STRING}' has insufficient blocks ({child.Bat.NumBlocks}) for the stated size ({dirent.Header.FILE_SIZE})");
|
||||
// return null;
|
||||
//}
|
||||
}
|
||||
|
||||
return child;
|
||||
|
||||
Reference in New Issue
Block a user