From 5aff2d0b1b60b1e8d0231502250de223d7ed82e0 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 9 Jan 2023 13:11:06 -0800 Subject: [PATCH] Add CFB models --- BurnOutSharp.Models/CFB/Binary.cs | 92 +++++++++++++++ BurnOutSharp.Models/CFB/Constants.cs | 7 ++ BurnOutSharp.Models/CFB/DirectoryEntry.cs | 133 ++++++++++++++++++++++ BurnOutSharp.Models/CFB/Enums.cs | 72 ++++++++++++ BurnOutSharp.Models/CFB/FileHeader.cs | 127 +++++++++++++++++++++ 5 files changed, 431 insertions(+) create mode 100644 BurnOutSharp.Models/CFB/Binary.cs create mode 100644 BurnOutSharp.Models/CFB/Constants.cs create mode 100644 BurnOutSharp.Models/CFB/DirectoryEntry.cs create mode 100644 BurnOutSharp.Models/CFB/Enums.cs create mode 100644 BurnOutSharp.Models/CFB/FileHeader.cs diff --git a/BurnOutSharp.Models/CFB/Binary.cs b/BurnOutSharp.Models/CFB/Binary.cs new file mode 100644 index 00000000..50f9faed --- /dev/null +++ b/BurnOutSharp.Models/CFB/Binary.cs @@ -0,0 +1,92 @@ +namespace BurnOutSharp.Models.CFB +{ + /// + /// Microsoft Compound File Binary (CFB) file format, also known as the + /// Object Linking and Embedding (OLE) or Component Object Model (COM) + /// structured storage compound file implementation binary file format. + /// This structure name can be shortened to compound file. + /// + /// + public sealed class Binary + { + /// + /// Compound file header + /// + public FileHeader Header { get; set; } + + /// + /// The FAT is the main allocator for space within a compound file. + /// Every sector in the file is represented within the FAT in some + /// fashion, including those sectors that are unallocated (free). + /// The FAT is a sector chain that is made up of one or more FAT sectors. + /// + /// + /// If Header Major Version is 3, there MUST be 128 fields specified to fill a 512-byte sector. + /// + /// If Header Major Version is 4, there MUST be 1,024 fields specified to fill a 4,096-byte sector + /// + public SectorNumber[] FATSectors { get; set; } + + /// + /// The mini FAT is used to allocate space in the mini stream. + /// The mini stream is divided intosmaller, equal-length sectors, + /// and the sector size that is used for the mini stream is specified + /// from the Compound File Header (64 bytes). + /// + /// + /// If Header Major Version is 3, there MUST be 128 fields specified to fill a 512-byte sector. + /// + /// If Header Major Version is 4, there MUST be 1,024 fields specified to fill a 4,096-byte sector + /// + public SectorNumber[] MiniFATSectors { get; set; } + + /// + /// The DIFAT array is used to represent storage of the FAT sectors. + /// The DIFAT is represented by an array of 32-bit sector numbers. + /// The DIFAT array is stored both in the header and in DIFAT sectors. + /// In the header, the DIFAT array occupies 109 entries, and in each + /// DIFAT sector, the DIFAT array occupies the entire sector minus + /// 4 bytes. (The last field is for chaining the DIFAT sector chain.) + /// + /// + /// If Header Major Version is 3, there MUST be 127 fields specified to + /// fill a 512-byte sector minus the "Next DIFAT Sector Location" field. + /// + /// If Header Major Version is 4, there MUST be 1,023 fields specified + /// to fill a 4,096-byte sector minus the "Next DIFAT Sector Location" field. + /// + public SectorNumber[] DIFATSectors { get; set; } + + /// + /// The directory entry array is an array of directory entries that + /// are grouped into a directory sector. Each storage object or stream + /// object within a compound file is represented by a single directory + /// entry. The space for the directory sectors that are holding the + /// array is allocated from the FAT. + /// + /// + /// The first entry in the first sector of the directory chain (also + /// referred to as the first element of the directory array, or stream + /// ID #0) is known as the root directory entry, and it is reserved for + /// two purposes. First, it provides a root parent for all objects that + /// are stationed at the root of the compound file. Second, its function + /// is overloaded to store the size and starting sector for the mini stream. + /// + /// The root directory entry behaves as both a stream and a storage object. + /// The root directory entry's Name field MUST contain the null-terminated + /// string "Root Entry" in Unicode UTF-16. + /// + /// The object class GUID (CLSID) that is stored in the root directory + /// entry can be used for COM activation of the document's application. + /// + /// The time stamps for the root storage are not maintained in the root + /// directory entry. Rather, the root storage's creation and modification + /// time stamps are normally stored on the file itself in the file system. + /// + /// The Creation Time field in the root storage directory entry MUST be + /// all zeroes. The Modified Time field in the root storage directory + /// entry MAY be all zeroes. + /// + public DirectoryEntry[] Directories { get; set; } + } +} \ No newline at end of file diff --git a/BurnOutSharp.Models/CFB/Constants.cs b/BurnOutSharp.Models/CFB/Constants.cs new file mode 100644 index 00000000..c08b61ed --- /dev/null +++ b/BurnOutSharp.Models/CFB/Constants.cs @@ -0,0 +1,7 @@ +namespace BurnOutSharp.Models.CFB +{ + public static class Constants + { + public static readonly byte[] Signature = new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 }; + } +} \ No newline at end of file diff --git a/BurnOutSharp.Models/CFB/DirectoryEntry.cs b/BurnOutSharp.Models/CFB/DirectoryEntry.cs new file mode 100644 index 00000000..09ac33eb --- /dev/null +++ b/BurnOutSharp.Models/CFB/DirectoryEntry.cs @@ -0,0 +1,133 @@ +using System; + +namespace BurnOutSharp.Models.CFB +{ + /// + public sealed class DirectoryEntry + { + /// + /// This field MUST contain a Unicode string for the storage or stream + /// name encoded in UTF-16. The name MUST be terminated with a UTF-16 + /// terminating null character. Thus, storage and stream names are limited + /// to 32 UTF-16 code points, including the terminating null character. + /// When locating an object in the compound file except for the root + /// storage, the directory entry name is compared by using a special + /// case-insensitive uppercase mapping, described in Red-Black Tree. + /// The following characters are illegal and MUST NOT be part of the + /// name: '/', '\', ':', '!'. + /// + public string Name; + + /// + /// This field MUST be 0x00, 0x01, 0x02, or 0x05, depending on the + /// actual type of object. All other values are not valid. + /// + public ushort NameLength; + + /// + /// This field MUST match the length of the Directory Entry Name Unicode + /// string in bytes. The length MUST be a multiple of 2 and include the + /// terminating null character in the count. This length MUST NOT exceed 64, + /// the maximum size of the Directory Entry Name field. + /// + public ObjectType ObjectType; + + /// + /// This field MUST be 0x00 (red) or 0x01 (black). All other values are not valid. + /// + public ColorFlag ColorFlag; + + /// + /// This field contains the stream ID of the left sibling. If there + /// is no left sibling, the field MUST be set to NOSTREAM (0xFFFFFFFF). + /// + public StreamID LeftSiblingID; + + /// + /// This field contains the stream ID of the right sibling. If there + /// is no right sibling, the field MUST be set to NOSTREAM (0xFFFFFFFF). + /// + public StreamID RightSiblingID; + + /// + /// This field contains the stream ID of a child object. If there is no + /// child object, including all entries for stream objects, the field + /// MUST be set to NOSTREAM (0xFFFFFFFF). + /// + public StreamID ChildID; + + /// + /// This field contains an object class GUID, if this entry is for a + /// storage object or root storage object. For a stream object, this field + /// MUST be set to all zeroes. A value containing all zeroes in a storage + /// or root storage directory entry is valid, and indicates that no object + /// class is associated with the storage. If an implementation of the file + /// format enables applications to create storage objects without explicitly + /// setting an object class GUID, it MUST write all zeroes by default. If + /// this value is not all zeroes, the object class GUID can be used as a + /// parameter to start applications. + /// + public Guid CLSID; + + /// + /// This field contains the user-defined flags if this entry is for a storage + /// object or root storage object. For a stream object, this field SHOULD be + /// set to all zeroes because many implementations provide no way for + /// applications to retrieve state bits from a stream object. If an + /// implementation of the file format enables applications to create storage + /// objects without explicitly setting state bits, it MUST write all zeroes + /// by default. + /// + public uint StateBits; + + /// + /// This field contains the creation time for a storage object, or all zeroes + /// to indicate that the creation time of the storage object was not recorded. + /// The Windows FILETIME structure is used to represent this field in UTC. + /// For a stream object, this field MUST be all zeroes. For a root storage + /// object, this field MUST be all zeroes, and the creation time is retrieved + /// or set on the compound file itself. + /// + public ulong CreationTime; + + /// + /// This field contains the modification time for a storage object, or all + /// zeroes to indicate that the modified time of the storage object was not + /// recorded. The Windows FILETIME structure is used to represent this field + /// in UTC. For a stream object, this field MUST be all zeroes. For a root + /// storage object, this field MAY<2> be set to all zeroes, and the modified + /// time is retrieved or set on the compound file itself. + /// + public ulong ModifiedTime; + + /// + /// This field contains the first sector location if this is a stream object. + /// For a root storage object, this field MUST contain the first sector of the + /// mini stream, if the mini stream exists. For a storage object, this field MUST + /// be set to all zeroes. + /// + public uint StartingSectorLocation; + + /// + /// This 64-bit integer field contains the size of the user-defined data if this + /// is a stream object. For a root storage object, this field contains the size + /// of the mini stream. For a storage object, this field MUST be set to all zeroes. + /// + /// + /// For a version 3 compound file 512-byte sector size, the value of this field MUST + /// be less than or equal to 0x80000000. (Equivalently, this requirement can be stated: + /// the size of a stream or of the mini stream in a version 3 compound file MUST be + /// less than or equal to 2 gigabytes (GB).) Note that as a consequence of this + /// requirement, the most significant 32 bits of this field MUST be zero in a version + /// 3 compound file. However, implementers should be aware that some older + /// implementations did not initialize the most significant 32 bits of this field, + /// and these bits might therefore be nonzero in files that are otherwise valid + /// version 3 compound files. Although this document does not normatively specify + /// parser behavior, it is recommended that parsers ignore the most significant 32 bits + /// of this field in version 3 compound files, treating it as if its value were zero, + /// unless there is a specific reason to do otherwise (for example, a parser whose + /// purpose is to verify the correctness of a compound file). + /// + public ulong StreamSize; + } +} \ No newline at end of file diff --git a/BurnOutSharp.Models/CFB/Enums.cs b/BurnOutSharp.Models/CFB/Enums.cs new file mode 100644 index 00000000..8b10211c --- /dev/null +++ b/BurnOutSharp.Models/CFB/Enums.cs @@ -0,0 +1,72 @@ +namespace BurnOutSharp.Models.CFB +{ + public enum ColorFlag : byte + { + Red = 0x00, + Black = 0x01, + } + + public enum ObjectType : byte + { + Unknown = 0x00, + StorageObject = 0x01, + StreamObject = 0x02, + RootStorageObject = 0x05, + } + + public enum SectorNumber : uint + { + /// + /// Regular sector number. + /// + REGSECT = 0x00000000, // 0x00000000 - 0xFFFFFFF9 + + /// + /// Maximum regular sector number. + /// + MAXREGSECT = 0xFFFFFFFA, + + /// + /// Reserved for future use. + /// + NotApplicable = 0xFFFFFFFB, + + /// + /// Specifies a DIFAT sector in the FAT. + /// + DIFSECT = 0xFFFFFFFC, + + /// + /// Specifies a FAT sector in the FAT. + /// + FATSECT = 0xFFFFFFFD, + + /// + /// End of a linked chain of sectors. + /// + ENDOFCHAIN = 0xFFFFFFFE, + + /// + /// Specifies an unallocated sector in the FAT, Mini FAT, or DIFAT. + /// + FREESECT = 0xFFFFFFFF, + } + + public enum StreamID : uint + { + /// + /// Regular stream ID to identify the directory entry. + /// + REGSID = 0x00000000, // 0x00000000 - 0xFFFFFFF9 + + /// + /// Maximum regular stream ID. + /// + MAXREGSID = 0xFFFFFFFA, + + /// + /// Terminator or empty pointer. + /// + NOSTREAM = 0xFFFFFFFF, + } +} \ No newline at end of file diff --git a/BurnOutSharp.Models/CFB/FileHeader.cs b/BurnOutSharp.Models/CFB/FileHeader.cs new file mode 100644 index 00000000..b8ea297a --- /dev/null +++ b/BurnOutSharp.Models/CFB/FileHeader.cs @@ -0,0 +1,127 @@ +using System; + +namespace BurnOutSharp.Models.CFB +{ + /// + public sealed class FileHeader + { + /// + /// IOdentification signature for the compound file structure, and MUST be + /// set to the value 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1. + /// + public byte[] Signature; + + /// + /// Reserved and unused class ID that MUST be set to all zeroes (CLSID_NULL) + /// + public Guid CLSID; + + /// + /// Version number for nonbreaking changes. This field SHOULD be set to + /// 0x003E if the major version field is either 0x0003 or 0x0004. + /// + public ushort MinorVersion; + + /// + /// Version number for breaking changes. This field MUST be set to either + /// 0x0003 (version 3) or 0x0004 (version 4). + /// + public ushort MajorVersion; + + /// + /// This field MUST be set to 0xFFFE. This field is a byte order mark for + /// all integer fields, specifying little-endian byte order. + /// + public ushort ByteOrder; + + /// + /// 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, the Sector Shift MUST be 0x0009, specifying a + /// sector size of 512 bytes. + /// + /// If Major Version is 4, the Sector Shift MUST be 0x000C, specifying a + /// sector size of 4096 bytes. + /// + public ushort SectorShift; + + /// + /// 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. + /// + public ushort MiniSectorShift; + + /// + /// This field MUST be set to all zeroes. + /// + public byte[] Reserved; + + /// + /// This integer field contains the count of the number of directory sectors + /// in the compound file. + /// + /// If Major Version is 3, the Number of Directory Sectors MUST be zero. This + /// field is not supported for version 3 compound files. + /// + public uint NumberOfDirectorySectors; + + /// + /// This integer field contains the count of the number of FAT sectors in the + /// compound file. + /// + public uint NumberOfFATSectors; + + /// + /// This integer field contains the starting sector number for the directory stream. + /// + public uint FirstDirectorySectorLocation; + + /// + /// This integer field MAY contain a sequence number that is incremented every time + /// the compound file is saved by an implementation that supports file transactions. + /// This is the field that MUST be set to all zeroes if file transactions are not + /// implemented. + /// + public uint TransactionSignatureNumber; + + /// + /// This integer field MUST be set to 0x00001000. This field specifies the maximum + /// size of a user-defined data stream that is allocated from the mini FAT and mini + /// stream, and that cutoff is 4,096 bytes. Any user-defined data stream that is + /// greater than or equal to this cutoff size must be allocated as normal sectors from + /// the FAT. + /// + public uint MiniStreamCutoffSize; + + /// + /// This integer field contains the starting sector number for the mini FAT. + /// + public uint FirstMiniFATSectorLocation; + + /// + /// This integer field contains the count of the number of mini FAT sectors in the + /// compound file. + /// + public uint NumberOfMiniFATSectors; + + /// + /// This integer field contains the starting sector number for the DIFAT. + /// + public uint FirstDIFATSectorLocation; + + /// + /// This integer field contains the count of the number of DIFAT sectors in the + /// compound file. + /// + public uint NumberOfDIFATSectors; + + /// + /// This array of 32-bit integer fields contains the first 109 FAT sector + /// locations of the compound file + /// + public uint[] DIFAT; + } +} \ No newline at end of file