diff --git a/BurnOutSharp.Builders/CFB.cs b/BurnOutSharp.Builders/CFB.cs
new file mode 100644
index 00000000..b95368ba
--- /dev/null
+++ b/BurnOutSharp.Builders/CFB.cs
@@ -0,0 +1,138 @@
+using System.IO;
+using System.Text;
+using BurnOutSharp.Models.CFB;
+using BurnOutSharp.Utilities;
+using static BurnOutSharp.Models.CFB.Constants;
+
+namespace BurnOutSharp.Builders
+{
+ public class CFB
+ {
+ #region Byte Data
+
+ ///
+ /// Parse a byte array into a Compound File Binary
+ ///
+ /// Byte array to parse
+ /// Offset into the byte array
+ /// Filled Compound File Binary on success, null on error
+ public static Binary ParseBinary(byte[] data, int offset)
+ {
+ // If the data is invalid
+ if (data == null)
+ return null;
+
+ // If the offset is out of bounds
+ if (offset < 0 || offset >= data.Length)
+ return null;
+
+ // Create a memory stream and parse that
+ MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset);
+ return ParseBinary(dataStream);
+ }
+
+ #endregion
+
+ #region Stream Data
+
+ ///
+ /// Parse a Stream into a Compound File Binary
+ ///
+ /// Stream to parse
+ /// Filled Compound File Binary on success, null on error
+ public static Binary ParseBinary(Stream data)
+ {
+ // If the data is invalid
+ if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead)
+ return null;
+
+ // If the offset is out of bounds
+ if (data.Position < 0 || data.Position >= data.Length)
+ return null;
+
+ // Cache the current offset
+ int initialOffset = (int)data.Position;
+
+ // Create a new binary to fill
+ var binary = new Binary();
+
+ #region Header
+
+ // Try to parse the file header
+ var fileHeader = ParseFileHeader(data);
+ if (fileHeader == null)
+ return null;
+
+ // Set the file header
+ binary.Header = fileHeader;
+
+ #endregion
+
+ // TODO: Implement FAT sector parsing
+ // TODO: Implement Mini FAT sector parsing
+ // TODO: Implement DIFAT sector parsing
+ // TODO: Implement directory sector parsing
+
+ return binary;
+ }
+
+ ///
+ /// Parse a Stream into a file header
+ ///
+ /// Stream to parse
+ /// Filled file header on success, null on error
+ private static FileHeader ParseFileHeader(Stream data)
+ {
+ // TODO: Use marshalling here instead of building
+ FileHeader header = new FileHeader();
+
+ header.Signature = data.ReadBytes(8);
+ if (header.Signature != SignatureBytes)
+ return null;
+
+ header.CLSID = data.ReadGuid();
+ header.MinorVersion = data.ReadUInt16();
+ header.MajorVersion = data.ReadUInt16();
+ header.ByteOrder = data.ReadUInt16();
+ if (header.ByteOrder != 0xFFFE)
+ return null;
+
+ header.SectorShift = data.ReadUInt16();
+ if (header.MajorVersion == 3 && header.SectorShift != 0x0009)
+ return null;
+ else if (header.MajorVersion == 4 && header.SectorShift != 0x000C)
+ return null;
+
+ header.MiniSectorShift = data.ReadUInt16();
+ header.Reserved = data.ReadBytes(6);
+ header.NumberOfDirectorySectors = data.ReadUInt32();
+ if (header.MajorVersion == 3 && header.NumberOfDirectorySectors != 0)
+ return null;
+
+ header.NumberOfFATSectors = data.ReadUInt32();
+ header.FirstDirectorySectorLocation = data.ReadUInt32();
+ header.TransactionSignatureNumber = data.ReadUInt32();
+ header.MiniStreamCutoffSize = data.ReadUInt32();
+ if (header.MiniStreamCutoffSize != 0x00001000)
+ return null;
+
+ header.FirstMiniFATSectorLocation = data.ReadUInt32();
+ header.NumberOfMiniFATSectors = data.ReadUInt32();
+ header.FirstDIFATSectorLocation = data.ReadUInt32();
+ header.NumberOfDIFATSectors = data.ReadUInt32();
+ header.DIFAT = new uint[109];
+ for (int i = 0; i < header.DIFAT.Length; i++)
+ {
+ header.DIFAT[i] = data.ReadUInt32();
+ }
+
+ // Skip rest of sector for version 4
+ if (header.MajorVersion == 4)
+ _ = data.ReadBytes(3584);
+
+ return header;
+ }
+
+ #endregion
+ }
+}
diff --git a/BurnOutSharp.Models/CFB/Constants.cs b/BurnOutSharp.Models/CFB/Constants.cs
index c08b61ed..59e93f01 100644
--- a/BurnOutSharp.Models/CFB/Constants.cs
+++ b/BurnOutSharp.Models/CFB/Constants.cs
@@ -2,6 +2,6 @@ namespace BurnOutSharp.Models.CFB
{
public static class Constants
{
- public static readonly byte[] Signature = new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
+ public static readonly byte[] SignatureBytes = new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
}
}
\ No newline at end of file
diff --git a/BurnOutSharp.Utilities/Extensions.cs b/BurnOutSharp.Utilities/Extensions.cs
index 9efc42a7..be63499a 100644
--- a/BurnOutSharp.Utilities/Extensions.cs
+++ b/BurnOutSharp.Utilities/Extensions.cs
@@ -271,6 +271,17 @@ namespace BurnOutSharp.Utilities
return value;
}
+ ///
+ /// Read a Guid from the stream
+ ///
+ public static Guid ReadGuid(this byte[] content, ref int offset)
+ {
+ byte[] buffer = new byte[16];
+ Array.Copy(content, offset, buffer, 0, 16);
+ offset += 16;
+ return new Guid(buffer);
+ }
+
///
/// Read a null-terminated string from the stream
///
@@ -409,6 +420,16 @@ namespace BurnOutSharp.Utilities
return BitConverter.ToUInt64(buffer, 0);
}
+ ///
+ /// Read a Guid from the stream
+ ///
+ public static Guid ReadGuid(this Stream stream)
+ {
+ byte[] buffer = new byte[16];
+ stream.Read(buffer, 0, 16);
+ return new Guid(buffer);
+ }
+
///
/// Read a null-terminated string from the stream
///