From 6fbb6bd8dcbb212c2e95cbe71f1c40e59b6ff8e3 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Mon, 9 Jan 2023 15:58:10 -0800 Subject: [PATCH] Implement directory section parsing --- BurnOutSharp.Builders/CFB.cs | 249 ++++++++++++++++++++------ BurnOutSharp.Models/CFB/Binary.cs | 8 +- BurnOutSharp.Models/CFB/FileHeader.cs | 2 +- 3 files changed, 200 insertions(+), 59 deletions(-) diff --git a/BurnOutSharp.Builders/CFB.cs b/BurnOutSharp.Builders/CFB.cs index 8d58f9fe..cfc9d9ae 100644 --- a/BurnOutSharp.Builders/CFB.cs +++ b/BurnOutSharp.Builders/CFB.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Text; using BurnOutSharp.Models.CFB; using BurnOutSharp.Utilities; using static BurnOutSharp.Models.CFB.Constants; @@ -69,98 +70,185 @@ namespace BurnOutSharp.Builders #endregion - #region FAT Sectors + #region DIFAT Sector Numbers + + // Create a DIFAT sector table + var difatSectors = new List(); + + // Add the sectors from the header + difatSectors.AddRange(fileHeader.DIFAT); + + // Loop through and add the DIFAT sectors + SectorNumber currentSector = (SectorNumber)fileHeader.FirstDIFATSectorLocation; + for (int i = 0; i < fileHeader.NumberOfDIFATSectors; i++) + { + // If we have a readable sector + if (currentSector <= SectorNumber.MAXREGSECT) + { + // Get the new next sector information + long sectorOffset = (long)((long)(currentSector + 1) * Math.Pow(2, fileHeader.SectorShift)); + if (sectorOffset < 0 || sectorOffset >= data.Length) + return null; + + // Seek to the next sector + data.Seek(sectorOffset, SeekOrigin.Begin); + + // Try to parse the sectors + var sectorNumbers = ParseSectorNumbers(data, fileHeader.SectorShift); + if (sectorNumbers == null) + return null; + + // Add the sector shifts + difatSectors.AddRange(sectorNumbers); + } + + // Get the next sector from the DIFAT + currentSector = difatSectors[i]; + } + + // Assign the DIFAT sectors table + binary.DIFATSectorNumbers = difatSectors.ToArray(); + + #endregion + + #region FAT Sector Numbers // Create a FAT sector table var fatSectors = new List(); // Loop through and add the FAT sectors + currentSector = binary.DIFATSectorNumbers[0]; for (int i = 0; i < fileHeader.NumberOfFATSectors; i++) { - // Try to get the FAT sector offset - long offset = (long)(fileHeader.DIFAT[i] * Math.Pow(2, fileHeader.SectorShift)); - if (offset < 0 || offset >= data.Length) - return null; + // If we have a readable sector + if (currentSector <= SectorNumber.MAXREGSECT) + { + // Get the new next sector information + long sectorOffset = (long)((long)(currentSector + 1) * Math.Pow(2, fileHeader.SectorShift)); + if (sectorOffset < 0 || sectorOffset >= data.Length) + return null; - // Seek to the offset - data.Seek(offset, SeekOrigin.Begin); + // Seek to the next sector + data.Seek(sectorOffset, SeekOrigin.Begin); - // Try to parse the sectors - var sectorNumbers = ParseSector(data, fileHeader.SectorShift); - if (sectorNumbers == null) - return null; + // Try to parse the sectors + var sectorNumbers = ParseSectorNumbers(data, fileHeader.SectorShift); + if (sectorNumbers == null) + return null; - // Add the sector shifts - fatSectors.AddRange(sectorNumbers); + // Add the sector shifts + fatSectors.AddRange(sectorNumbers); + } + + // Get the next sector from the DIFAT + currentSector = binary.DIFATSectorNumbers[i]; } // Assign the FAT sectors table - binary.FATSectors = fatSectors.ToArray(); + binary.FATSectorNumbers = fatSectors.ToArray(); #endregion - #region Mini FAT Sectors - - // Get the offset of the first mini FAT sector - long firstMiniFatOffset = (long)(fileHeader.FirstMiniFATSectorLocation * Math.Pow(2, fileHeader.SectorShift)); - if (firstMiniFatOffset < 0 || firstMiniFatOffset >= data.Length) - return null; - - // Seek to the first mini FAT sector - data.Seek(firstMiniFatOffset, SeekOrigin.Begin); + #region Mini FAT Sector Numbers // Create a mini FAT sector table var miniFatSectors = new List(); // Loop through and add the mini FAT sectors + currentSector = (SectorNumber)fileHeader.FirstMiniFATSectorLocation; for (int i = 0; i < fileHeader.NumberOfMiniFATSectors; i++) { - // Try to parse the sectors - var sectorNumbers = ParseSector(data, fileHeader.SectorShift); - if (sectorNumbers == null) - return null; + // If we have a readable sector + if (currentSector <= SectorNumber.MAXREGSECT) + { + // Get the new next sector information + long sectorOffset = (long)((long)(currentSector + 1) * Math.Pow(2, fileHeader.SectorShift)); + if (sectorOffset < 0 || sectorOffset >= data.Length) + return null; - // Add the sector shifts - miniFatSectors.AddRange(sectorNumbers); + // Seek to the next sector + data.Seek(sectorOffset, SeekOrigin.Begin); + + // Try to parse the sectors + var sectorNumbers = ParseSectorNumbers(data, fileHeader.SectorShift); + if (sectorNumbers == null) + return null; + + // Add the sector shifts + miniFatSectors.AddRange(sectorNumbers); + } + + // Get the next sector from the DIFAT + currentSector = binary.DIFATSectorNumbers[i]; } // Assign the mini FAT sectors table - binary.MiniFATSectors = miniFatSectors.ToArray(); + binary.MiniFATSectorNumbers = miniFatSectors.ToArray(); #endregion - #region DIFAT Sectors + #region Directory Entries - // Get the offset of the first DIFAT sector - long firstDifatOffset = (long)(fileHeader.FirstDIFATSectorLocation * Math.Pow(2, fileHeader.SectorShift)); - if (firstDifatOffset < 0 || firstDifatOffset >= data.Length) + // Get the offset of the first directory sector + long firstDirectoryOffset = (long)(fileHeader.FirstDirectorySectorLocation * Math.Pow(2, fileHeader.SectorShift)); + if (firstDirectoryOffset < 0 || firstDirectoryOffset >= data.Length) return null; - // Seek to the first DIFAT sector - data.Seek(firstDifatOffset, SeekOrigin.Begin); + // Seek to the first directory sector + data.Seek(firstDirectoryOffset, SeekOrigin.Begin); - // Create a DIFAT sector table - var difatSectors = new List(); + // Create a directory sector table + var directorySectors = new List(); - // Loop through and add the DIFAT sectors - for (int i = 0; i < fileHeader.NumberOfMiniFATSectors; i++) + // Get the number of directory sectors + uint directorySectorCount = 0; + switch (fileHeader.MajorVersion) { - // Try to parse the sectors - var sectorNumbers = ParseSector(data, fileHeader.SectorShift); - if (sectorNumbers == null) - return null; - - // Add the sector shifts - difatSectors.AddRange(sectorNumbers); + case 3: + directorySectorCount = int.MaxValue; + break; + case 4: + directorySectorCount = fileHeader.NumberOfDirectorySectors; + break; } - // Assign the DIFAT sectors table - binary.DIFATSectors = difatSectors.ToArray(); + // Loop through and add the directory sectors + currentSector = (SectorNumber)fileHeader.FirstDirectorySectorLocation; + for (int i = 0; i < directorySectorCount; i++) + { + // If we have an end of chain + if (currentSector == SectorNumber.ENDOFCHAIN) + break; + + // If we have a readable sector + if (currentSector <= SectorNumber.MAXREGSECT) + { + // Get the new next sector information + long sectorOffset = (long)((long)(currentSector + 1) * Math.Pow(2, fileHeader.SectorShift)); + if (sectorOffset < 0 || sectorOffset >= data.Length) + return null; + + // Seek to the next sector + data.Seek(sectorOffset, SeekOrigin.Begin); + + // Try to parse the sectors + var directoryEntries = ParseDirectoryEntries(data, fileHeader.SectorShift); + if (directoryEntries == null) + return null; + + // Add the sector shifts + directorySectors.AddRange(directoryEntries); + } + + // Get the next sector from the DIFAT + currentSector = binary.DIFATSectorNumbers[i]; + } + + // Assign the Directory sectors table + binary.DirectoryEntries = directorySectors.ToArray(); #endregion - // TODO: Implement directory sector parsing - return binary; } @@ -208,10 +296,10 @@ namespace BurnOutSharp.Builders header.NumberOfMiniFATSectors = data.ReadUInt32(); header.FirstDIFATSectorLocation = data.ReadUInt32(); header.NumberOfDIFATSectors = data.ReadUInt32(); - header.DIFAT = new uint[109]; + header.DIFAT = new SectorNumber[109]; for (int i = 0; i < header.DIFAT.Length; i++) { - header.DIFAT[i] = data.ReadUInt32(); + header.DIFAT[i] = (SectorNumber)data.ReadUInt32(); } // Skip rest of sector for version 4 @@ -227,7 +315,7 @@ namespace BurnOutSharp.Builders /// Stream to parse /// Sector shift from the header /// Filled sector full of sector numbers on success, null on error - private static SectorNumber[] ParseSector(Stream data, ushort sectorShift) + private static SectorNumber[] ParseSectorNumbers(Stream data, ushort sectorShift) { // TODO: Use marshalling here instead of building int sectorCount = (int)(Math.Pow(2, sectorShift) / sizeof(uint)); @@ -241,6 +329,59 @@ namespace BurnOutSharp.Builders return sectorNumbers; } + /// + /// Parse a Stream into a sector full of directory entries + /// + /// Stream to parse + /// Sector shift from the header + /// Filled sector full of directory entries on success, null on error + private static DirectoryEntry[] ParseDirectoryEntries(Stream data, ushort sectorShift) + { + // TODO: Use marshalling here instead of building + const int directoryEntrySize = 64 + 2 + 1 + 1 + 4 + 4 + 4 + 16 + 4 + 8 + 8 + 4 + 8; + int sectorCount = (int)(Math.Pow(2, sectorShift) / directoryEntrySize); + DirectoryEntry[] directoryEntries = new DirectoryEntry[sectorCount]; + + for (int i = 0; i < directoryEntries.Length; i++) + { + var directoryEntry = ParseDirectoryEntry(data); + if (directoryEntry == null) + return null; + + directoryEntries[i] = directoryEntry; + } + + return directoryEntries; + } + + /// + /// Parse a Stream into a directory entry + /// + /// Stream to parse + /// Filled directory entry on success, null on error + private static DirectoryEntry ParseDirectoryEntry(Stream data) + { + // TODO: Use marshalling here instead of building + DirectoryEntry directoryEntry = new DirectoryEntry(); + + byte[] name = data.ReadBytes(64); + directoryEntry.Name = Encoding.Unicode.GetString(name).TrimEnd('\0'); + directoryEntry.NameLength = data.ReadUInt16(); + directoryEntry.ObjectType = (ObjectType)data.ReadByteValue(); + directoryEntry.ColorFlag = (ColorFlag)data.ReadByteValue(); + directoryEntry.LeftSiblingID = (StreamID)data.ReadUInt32(); + directoryEntry.RightSiblingID = (StreamID)data.ReadUInt32(); + directoryEntry.ChildID = (StreamID)data.ReadUInt32(); + directoryEntry.CLSID = data.ReadGuid(); + directoryEntry.StateBits = data.ReadUInt32(); + directoryEntry.CreationTime = data.ReadUInt64(); + directoryEntry.ModifiedTime = data.ReadUInt64(); + directoryEntry.StartingSectorLocation = data.ReadUInt32(); + directoryEntry.StreamSize = data.ReadUInt64(); + + return directoryEntry; + } + #endregion } } diff --git a/BurnOutSharp.Models/CFB/Binary.cs b/BurnOutSharp.Models/CFB/Binary.cs index 50f9faed..f5ef1f68 100644 --- a/BurnOutSharp.Models/CFB/Binary.cs +++ b/BurnOutSharp.Models/CFB/Binary.cs @@ -25,7 +25,7 @@ namespace BurnOutSharp.Models.CFB /// /// 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; } + public SectorNumber[] FATSectorNumbers { get; set; } /// /// The mini FAT is used to allocate space in the mini stream. @@ -38,7 +38,7 @@ namespace BurnOutSharp.Models.CFB /// /// 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; } + public SectorNumber[] MiniFATSectorNumbers { get; set; } /// /// The DIFAT array is used to represent storage of the FAT sectors. @@ -55,7 +55,7 @@ namespace BurnOutSharp.Models.CFB /// 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; } + public SectorNumber[] DIFATSectorNumbers { get; set; } /// /// The directory entry array is an array of directory entries that @@ -87,6 +87,6 @@ namespace BurnOutSharp.Models.CFB /// all zeroes. The Modified Time field in the root storage directory /// entry MAY be all zeroes. /// - public DirectoryEntry[] Directories { get; set; } + public DirectoryEntry[] DirectoryEntries { get; set; } } } \ No newline at end of file diff --git a/BurnOutSharp.Models/CFB/FileHeader.cs b/BurnOutSharp.Models/CFB/FileHeader.cs index 931024e2..a772ed98 100644 --- a/BurnOutSharp.Models/CFB/FileHeader.cs +++ b/BurnOutSharp.Models/CFB/FileHeader.cs @@ -122,6 +122,6 @@ namespace BurnOutSharp.Models.CFB /// This array of 32-bit integer fields contains the first 109 FAT sector /// locations of the compound file /// - public uint[] DIFAT; + public SectorNumber[] DIFAT; } } \ No newline at end of file