using System;
using System.IO;
namespace CompoundFileBinary
{
public class ZBiffHeader
{
/// <summary>
/// Header Signature (8 bytes): Identification signature for the
/// compound file structure, and MUST be set to the value 0xD0, 0xCF,
/// 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1.
/// </summary>
public byte[] abSig;
/// <summary>
/// Header CLSID (16 bytes): Reserved and unused class ID that MUST
/// be set to all zeros (CLSID_NULL).
/// </summary>
public Guid clid;
/// <summary>
/// Minor Version (2 bytes): Version number for non-breaking changes.
/// This field SHOULD be set to 0x003E if the major version field is
/// either 0x0003 or 0x0004.
/// </summary>
public UInt16 uMinorVersion;
/// <summary>
/// Major Version (2 bytes): Version number for breaking changes.
/// This field MUST be set to either 0x0003 (version 3) or 0x0004
/// (version 4).
/// </summary>
public UInt16 uDllVersion;
/// <summary>
/// Byte Order (2 bytes): This field MUST be set to 0xFFFE. This
/// field is a byte order mark for all integer fields, specifying
/// little-endian byte order.
/// </summary>
public UInt16 uByteOrder;
/// <summary>
/// Sector Shift (2 bytes): This field MUST be set to 0x0009, or
/// 0x000c, depending on the Major Version field. This field specifies
/// the sector size of the compound file as a power of 2.
/// *If Major Version is 3, then the Sector Shift MUST be 0x0009,
/// specifying a sector size of 512 bytes.
/// * If Major Version is 4, then the Sector Shift MUST be 0x000C,
/// specifying a sector size of 4096 bytes.
/// </summary>
public UInt16 uSectorShift;
/// <summary>
/// Mini Sector Shift (2 bytes): This field MUST be set to 0x0006. This
/// field specifies the sector size of the Mini Stream as a power of 2.
/// The sector size of the Mini Stream MUST be 64 bytes.
/// </summary>
public UInt16 uMiniSectorShift;
/// <summary>
/// Reserved (6 bytes): This field MUST be set to all zeros.
/// </summary>
public byte[] abReserved;
/// <summary>
/// Number of Directory Sectors (4 bytes): This integer field contains
/// the count of the number of directory sectors in the compound file.
/// * If Major Version is 3, then the Number of Directory Sectors MUST
/// be zero. This field is not supported for version 3 compound
/// files.
/// </summary>
public UInt32 csectDir;
/// <summary>
/// Number of FAT Sectors (4 bytes): This integer field contains the
/// count of the number of FAT sectors in the compound file.
/// </summary>
public UInt32 csectFat;
/// <summary>
/// First Directory Sector Location (4 bytes): This integer field
/// contains the starting sector number for the directory stream.
/// </summary>
public UInt32 sectDirStart;
/// <summary>
/// Transaction Signature Number (4 bytes): This integer field is
/// intended to contain a sequence number that is incremented every
/// time the compound file is saved. Forever this implementation does
/// not support transactions.
/// </summary>
public UInt32 signature;
/// <summary>
/// Mini Stream Cutoff Size (4 bytes): This integer field MUST be set
/// to 0x00001000. This field specifies the maximum size of a user-
/// defined data stream allocated from the mini FAT and mini stream,
/// and that cutoff is 4096 bytes. Any user-defined data stream larger
/// than or equal to this cutoff size must be allocated as normal
/// sectors from the FAT.
/// </summary>
public UInt32 ulMiniSectorCutoff;
/// <summary>
/// First Mini FAT Sector Location (4 bytes): This integer field
/// contains the starting sector number for the mini FAT.
/// </summary>
public UInt32 sectMiniFatStart;
/// <summary>
/// Number of Mini FAT Sectors (4 bytes): This integer field contains
/// the count of the number of mini FAT sectors in the compound file.
/// </summary>
public UInt32 csectMiniFat;
/// <summary>
/// First DIFAT Sector Location (4 bytes): This integer field contains
/// the starting sector number for the DIFAT.
/// </summary>
public UInt32 sectDifStart;
/// <summary>
/// Number of DIFAT Sectors (4 bytes): This integer field contains the
/// count of the number of DIFAT sectors in the compound file.
/// </summary>
public UInt32 csectDif;
/// <summary>
/// DIFAT (4 bytes): This array of 32-bit integer fields contains the
/// first 109 FAT sector locations of the compound file.
/// * For version 4 compound files, the header size (512 bytes) is less
/// than the sector size (4096 bytes), so the remaining part of the
/// header (3584 bytes) MUST be filled with all zeros.
/// </summary>
public UInt32[] sectFat;
public void Read(BinaryReader br)
{
abSig = br.ReadBytes(8);
clid = new Guid(br.ReadInt32(), br.ReadInt16(), br.ReadInt16(), br.ReadBytes(8));
if (clid != Guid.Empty)
throw new Exception("Reserved and unused class ID that MUST be set to all zeros (CLSID_NULL).");
uMinorVersion = br.ReadUInt16();
if (uMinorVersion != 0x003E)
throw new Exception("Only major version 3 and 4 are supported which mean that minor version must be 0x003E.");
uDllVersion = br.ReadUInt16();
if (uDllVersion != 0x0003 && uDllVersion != 0x0004)
throw new Exception("Only major version 3 and 4 are supported.");
uByteOrder = br.ReadUInt16();
if (uByteOrder != 0xFFFE)
throw new Exception("Only little-endian byte order is supported.");
uSectorShift = br.ReadUInt16();
if (uDllVersion == 0x0003 && uSectorShift != 0x0009)
throw new Exception("In Major Version 3, the Sector Shift MUST be 0x0009.");
if (uDllVersion == 0x0004 && uSectorShift != 0x000C)
throw new Exception("In Major Version 4, the Sector Shift MUST be 0x000C.");
uMiniSectorShift = br.ReadUInt16();
abReserved = br.ReadBytes(6);
if (!IsZero(abReserved))
throw new Exception("Reserve byte must be zeros.");
csectDir = br.ReadUInt32();
if (uDllVersion == 0x0003 && csectDir != 0)
throw new Exception("In Major Version 3, csectDir must be zero.");
csectFat = br.ReadUInt32();
sectDirStart = br.ReadUInt32();
signature = br.ReadUInt32();
ulMiniSectorCutoff = br.ReadUInt32();
if (ulMiniSectorCutoff != 0x00001000)
throw new Exception("ulMiniSectorCutoff MUST be set to 0x00001000.");
sectMiniFatStart = br.ReadUInt32();
csectMiniFat = br.ReadUInt32();
sectDifStart = br.ReadUInt32();
csectDif = br.ReadUInt32();
sectFat = new UInt32[109];
for (int i = 0; i < 109; i++)
sectFat[i] = br.ReadUInt32();
// Padding for major version 4.
if (uDllVersion == 0x0004)
br.ReadBytes(3584);
}
bool IsZero(byte[] bytearray)
{
foreach (byte b in bytearray)
if (b != 0)
return false;
return true;
}
public ulong SectorSize
{
get { return (ulong)(1 << (int)uSectorShift); }
}
public ulong MiniSectorSize
{
get { return (ulong)(1 << (int)uMiniSectorShift); }
}
public static ulong Pow(uint factor, uint power)
{
if (power == 0)
return 1;
ulong r = factor;
while (power > 1)
{
r *= factor;
power--;
}
return r;
}
}
}