From 374f286585699623dc5c836c5a308a9f0179e516 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Sun, 25 Dec 2022 21:27:06 -0800 Subject: [PATCH] Add GCF wrapper --- BurnOutSharp.Builders/GCF.cs | 43 +- BurnOutSharp.Models/GCF/DirectoryEntry.cs | 5 + BurnOutSharp.Models/GCF/File.cs | 4 +- BurnOutSharp.Wrappers/BSP.cs | 10 +- BurnOutSharp.Wrappers/GCF.cs | 871 ++++++++++++++++++++++ BurnOutSharp.Wrappers/VPK.cs | 10 +- Test/Program.cs | 39 +- 7 files changed, 962 insertions(+), 20 deletions(-) create mode 100644 BurnOutSharp.Wrappers/GCF.cs diff --git a/BurnOutSharp.Builders/GCF.cs b/BurnOutSharp.Builders/GCF.cs index ac5225ed..af2d170c 100644 --- a/BurnOutSharp.Builders/GCF.cs +++ b/BurnOutSharp.Builders/GCF.cs @@ -1,3 +1,4 @@ +using System.Collections.Generic; using System.IO; using System.Text; using BurnOutSharp.Models.GCF; @@ -216,9 +217,38 @@ namespace BurnOutSharp.Builders #region Directory Names - // Read the directory names as a single string - byte[] directoryNames = data.ReadBytes((int)directoryHeader.NameSize); - file.DirectoryNames = Encoding.ASCII.GetString(directoryNames); + if (directoryHeader.NameSize > 0) + { + // Get the current offset for adjustment + long directoryNamesStart = data.Position; + + // Get the ending offset + long directoryNamesEnd = data.Position + directoryHeader.NameSize; + + // Create the string dictionary + file.DirectoryNames = new Dictionary(); + + // Loop and read the null-terminated strings + while (data.Position < directoryNamesEnd) + { + long nameOffset = data.Position - directoryNamesStart; + string directoryName = data.ReadString(Encoding.ASCII); + if (data.Position > directoryNamesEnd) + { + data.Seek(-directoryName.Length, SeekOrigin.Current); + byte[] endingData = data.ReadBytes((int)(directoryNamesEnd - data.Position)); + directoryName = Encoding.ASCII.GetString(endingData); + } + + file.DirectoryNames[nameOffset] = directoryName; + } + + // Loop and assign to entries + foreach (var directoryEntry in file.DirectoryEntries) + { + directoryEntry.Name = file.DirectoryNames[directoryEntry.NameOffset]; + } + } #endregion @@ -652,7 +682,7 @@ namespace BurnOutSharp.Builders return null; directoryMapHeader.Dummy1 = data.ReadUInt32(); - if (directoryMapHeader.Dummy0 != 0x00000000) + if (directoryMapHeader.Dummy1 != 0x00000000) return null; return directoryMapHeader; @@ -684,6 +714,9 @@ namespace BurnOutSharp.Builders ChecksumHeader checksumHeader = new ChecksumHeader(); checksumHeader.Dummy0 = data.ReadUInt32(); + if (checksumHeader.Dummy0 != 0x00000001) + return null; + checksumHeader.ChecksumSize = data.ReadUInt32(); return checksumHeader; @@ -704,7 +737,7 @@ namespace BurnOutSharp.Builders return null; checksumMapHeader.Dummy1 = data.ReadUInt32(); - if (checksumMapHeader.Dummy0 != 0x00000001) + if (checksumMapHeader.Dummy1 != 0x00000001) return null; checksumMapHeader.ItemCount = data.ReadUInt32(); diff --git a/BurnOutSharp.Models/GCF/DirectoryEntry.cs b/BurnOutSharp.Models/GCF/DirectoryEntry.cs index 9106ac18..1c72a2ca 100644 --- a/BurnOutSharp.Models/GCF/DirectoryEntry.cs +++ b/BurnOutSharp.Models/GCF/DirectoryEntry.cs @@ -8,6 +8,11 @@ namespace BurnOutSharp.Models.GCF /// public uint NameOffset; + /// + /// Directory item name from the end of the directory items. + /// + public string Name; + /// /// Size of the item. (If file, file size. If folder, num items.) /// diff --git a/BurnOutSharp.Models/GCF/File.cs b/BurnOutSharp.Models/GCF/File.cs index e736dbf4..b819764c 100644 --- a/BurnOutSharp.Models/GCF/File.cs +++ b/BurnOutSharp.Models/GCF/File.cs @@ -1,3 +1,5 @@ +using System.Collections.Generic; + namespace BurnOutSharp.Models.GCF { /// @@ -56,7 +58,7 @@ namespace BurnOutSharp.Models.GCF /// /// Directory names data /// - public string DirectoryNames { get; set; } + public Dictionary DirectoryNames { get; set; } /// /// Directory info 1 entries data diff --git a/BurnOutSharp.Wrappers/BSP.cs b/BurnOutSharp.Wrappers/BSP.cs index 94bf22d9..5a84e9ca 100644 --- a/BurnOutSharp.Wrappers/BSP.cs +++ b/BurnOutSharp.Wrappers/BSP.cs @@ -64,11 +64,11 @@ namespace BurnOutSharp.Wrappers private BSP() { } /// - /// Create an BSP from a byte array and offset + /// Create a BSP from a byte array and offset /// /// Byte array representing the BSP /// Offset within the array to parse - /// An BSP wrapper on success, null on failure + /// A BSP wrapper on success, null on failure public static BSP Create(byte[] data, int offset) { // If the data is invalid @@ -85,10 +85,10 @@ namespace BurnOutSharp.Wrappers } /// - /// Create anVPK from a Stream + /// Create a BSP from a Stream /// - /// Stream representing the executable - /// An VPK wrapper on success, null on failure + /// Stream representing the BSP + /// An BSP wrapper on success, null on failure public static BSP Create(Stream data) { // If the data is invalid diff --git a/BurnOutSharp.Wrappers/GCF.cs b/BurnOutSharp.Wrappers/GCF.cs new file mode 100644 index 00000000..115803f5 --- /dev/null +++ b/BurnOutSharp.Wrappers/GCF.cs @@ -0,0 +1,871 @@ +using System; +using System.IO; +using System.Linq; +using BurnOutSharp.Utilities; + +namespace BurnOutSharp.Wrappers +{ + public class GCF : WrapperBase + { + #region Pass-Through Properties + + #region Header + + /// + public uint Dummy0 => _file.Header.Dummy0; + + /// + public uint MajorVersion => _file.Header.MajorVersion; + + /// + public uint MinorVersion => _file.Header.MinorVersion; + + /// + public uint CacheID => _file.Header.CacheID; + + /// + public uint LastVersionPlayed => _file.Header.LastVersionPlayed; + + /// + public uint Dummy1 => _file.Header.Dummy1; + + /// + public uint Dummy2 => _file.Header.Dummy2; + + /// + public uint FileSize => _file.Header.FileSize; + + /// + public uint BlockSize => _file.Header.BlockSize; + + /// + public uint BlockCount => _file.Header.BlockCount; + + /// + public uint Dummy3 => _file.Header.Dummy3; + + #endregion + + #region Block Entry Header + + /// + public uint BEH_BlockCount => _file.BlockEntryHeader.BlockCount; + + /// + public uint BEH_BlocksUsed => _file.BlockEntryHeader.BlocksUsed; + + /// + public uint BEH_Dummy0 => _file.BlockEntryHeader.Dummy0; + + /// + public uint BEH_Dummy1 => _file.BlockEntryHeader.Dummy1; + + /// + public uint BEH_Dummy2 => _file.BlockEntryHeader.Dummy2; + + /// + public uint BEH_Dummy3 => _file.BlockEntryHeader.Dummy3; + + /// + public uint BEH_Dummy4 => _file.BlockEntryHeader.Dummy4; + + /// + public uint BEH_Checksum => _file.BlockEntryHeader.Checksum; + + #endregion + + #region Block Entries + + /// + public Models.GCF.BlockEntry[] BlockEntries => _file.BlockEntries; + + #endregion + + #region Fragmentation Map Header + + /// + public uint FMH_BlockCount => _file.FragmentationMapHeader.BlockCount; + + /// + public uint FMH_FirstUnusedEntry => _file.FragmentationMapHeader.FirstUnusedEntry; + + /// + public uint FMH_Terminator => _file.FragmentationMapHeader.Terminator; + + /// + public uint FMH_Checksum => _file.FragmentationMapHeader.Checksum; + + #endregion + + #region Fragmentation Maps + + /// + public Models.GCF.FragmentationMap[] FragmentationMaps => _file.FragmentationMaps; + + #endregion + + #region Block Entry Map Header + + /// + public uint? BEMH_BlockCount => _file.BlockEntryMapHeader?.BlockCount; + + /// + public uint? BEMH_FirstBlockEntryIndex => _file.BlockEntryMapHeader?.FirstBlockEntryIndex; + + /// + public uint? BEMH_LastBlockEntryIndex => _file.BlockEntryMapHeader?.LastBlockEntryIndex; + + /// + public uint? BEMH_Dummy0 => _file.BlockEntryMapHeader?.Dummy0; + + /// + public uint? BEMH_Checksum => _file.BlockEntryMapHeader?.Checksum; + + #endregion + + #region Block Entry Maps + + /// + public Models.GCF.BlockEntryMap[] BlockEntryMaps => _file.BlockEntryMaps; + + #endregion + + #region Directory Header + + /// + public uint DH_Dummy0 => _file.DirectoryHeader.Dummy0; + + /// + public uint DH_CacheID => _file.DirectoryHeader.CacheID; + + /// + public uint DH_LastVersionPlayed => _file.DirectoryHeader.LastVersionPlayed; + + /// + public uint DH_ItemCount => _file.DirectoryHeader.ItemCount; + + /// + public uint DH_FileCount => _file.DirectoryHeader.FileCount; + + /// + public uint DH_Dummy1 => _file.DirectoryHeader.Dummy1; + + /// + public uint DH_DirectorySize => _file.DirectoryHeader.DirectorySize; + + /// + public uint DH_NameSize => _file.DirectoryHeader.NameSize; + + /// + public uint DH_Info1Count => _file.DirectoryHeader.Info1Count; + + /// + public uint DH_CopyCount => _file.DirectoryHeader.CopyCount; + + /// + public uint DH_LocalCount => _file.DirectoryHeader.LocalCount; + + /// + public uint DH_Dummy2 => _file.DirectoryHeader.Dummy2; + + /// + public uint DH_Dummy3 => _file.DirectoryHeader.Dummy3; + + /// + public uint DH_Checksum => _file.DirectoryHeader.Checksum; + + #endregion + + #region Directory Entries + + /// + public Models.GCF.DirectoryEntry[] DirectoryEntries => _file.DirectoryEntries; + + #endregion + + #region Directory Names + + /// + public System.Collections.Generic.Dictionary DirectoryNames => _file.DirectoryNames; + + #endregion + + #region Directory Info 1 Entries + + /// + public Models.GCF.DirectoryInfo1Entry[] DirectoryInfo1Entries => _file.DirectoryInfo1Entries; + + #endregion + + #region Directory Info 2 Entries + + /// + public Models.GCF.DirectoryInfo2Entry[] DirectoryInfo2Entries => _file.DirectoryInfo2Entries; + + #endregion + + #region Directory Copy Entries + + /// + public Models.GCF.DirectoryCopyEntry[] DirectoryCopyEntries => _file.DirectoryCopyEntries; + + #endregion + + #region Directory Local Entries + + /// + public Models.GCF.DirectoryLocalEntry[] DirectoryLocalEntries => _file.DirectoryLocalEntries; + + #endregion + + #region Directory Map Header + + /// + public uint? DMH_Dummy0 => _file.DirectoryMapHeader?.Dummy0; + + /// + public uint? DMH_Dummy1 => _file.DirectoryMapHeader?.Dummy1; + + #endregion + + #region Directory Map Entries + + /// + public Models.GCF.DirectoryMapEntry[] DirectoryMapEntries => _file.DirectoryMapEntries; + + #endregion + + #region Checksum Header + + /// + public uint CH_Dummy0 => _file.ChecksumHeader.Dummy0; + + /// + public uint CH_ChecksumSize => _file.ChecksumHeader.ChecksumSize; + + #endregion + + #region Checksum Map Header + + /// + public uint CMH_Dummy0 => _file.ChecksumMapHeader.Dummy0; + + /// + public uint CMH_Dummy1 => _file.ChecksumMapHeader.Dummy1; + + /// + public uint CMH_ItemCount => _file.ChecksumMapHeader.ItemCount; + + /// + public uint CMH_ChecksumCount => _file.ChecksumMapHeader.ChecksumCount; + + #endregion + + #region Checksum Map Entries + + /// + public Models.GCF.ChecksumMapEntry[] ChecksumMapEntries => _file.ChecksumMapEntries; + + #endregion + + #region Checksum Entries + + /// + public Models.GCF.ChecksumEntry[] ChecksumEntries => _file.ChecksumEntries; + + #endregion + + #region Data Block Header + + /// + public uint DBH_LastVersionPlayed => _file.DataBlockHeader.LastVersionPlayed; + + /// + public uint DBH_BlockCount => _file.DataBlockHeader.BlockCount; + + /// + public uint DBH_BlockSize => _file.DataBlockHeader.BlockSize; + + /// + public uint DBH_FirstBlockOffset => _file.DataBlockHeader.FirstBlockOffset; + + /// + public uint DBH_BlocksUsed => _file.DataBlockHeader.BlocksUsed; + + /// + public uint DBH_Checksum => _file.DataBlockHeader.Checksum; + + #endregion + + #endregion + + #region Extension Properties + + // TODO: Figure out what extension properties are needed + + #endregion + + #region Instance Variables + + /// + /// Internal representation of the GCF + /// + private Models.GCF.File _file; + + #endregion + + #region Constructors + + /// + /// Private constructor + /// + private GCF() { } + + /// + /// Create an GCF from a byte array and offset + /// + /// Byte array representing the GCF + /// Offset within the array to parse + /// An GCF wrapper on success, null on failure + public static GCF Create(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 use that + MemoryStream dataStream = new MemoryStream(data, offset, data.Length - offset); + return Create(dataStream); + } + + /// + /// Create a GCF from a Stream + /// + /// Stream representing the GCF + /// An GCF wrapper on success, null on failure + public static GCF Create(Stream data) + { + // If the data is invalid + if (data == null || data.Length == 0 || !data.CanSeek || !data.CanRead) + return null; + + var file = Builders.GCF.ParseFile(data); + if (file == null) + return null; + + var wrapper = new GCF + { + _file = file, + _dataSource = DataSource.Stream, + _streamData = data, + }; + return wrapper; + } + + #endregion + + #region Printing + + /// + public override void Print() + { + Console.WriteLine("GCF Information:"); + Console.WriteLine("-------------------------"); + Console.WriteLine(); + + // Header + PrintHeader(); + + // Block Entries + PrintBlockEntryHeader(); + PrintBlockEntries(); + + // Fragmentation Maps + PrintFragmentationMapHeader(); + PrintFragmentationMaps(); + + // Block Entry Maps + PrintBlockEntryMapHeader(); + PrintBlockEntryMaps(); + + // Directory and Directory Maps + PrintDirectoryHeader(); + PrintDirectoryEntries(); + // TODO: Should we print out the entire string table? + PrintDirectoryInfo1Entries(); + PrintDirectoryInfo2Entries(); + PrintDirectoryCopyEntries(); + PrintDirectoryLocalEntries(); + PrintDirectoryMapHeader(); + PrintDirectoryMapEntries(); + + // Checksums and Checksum Maps + PrintChecksumHeader(); + PrintChecksumMapHeader(); + PrintChecksumMapEntries(); + PrintChecksumEntries(); + + // Data Blocks + PrintDataBlockHeader(); + } + + /// + /// Print header information + /// + private void PrintHeader() + { + Console.WriteLine(" Header Information:"); + Console.WriteLine(" -------------------------"); + Console.WriteLine($" Dummy 0: {Dummy0}"); + Console.WriteLine($" Major version: {MajorVersion}"); + Console.WriteLine($" Minor version: {MinorVersion}"); + Console.WriteLine($" Cache ID: {CacheID}"); + Console.WriteLine($" Last version played: {LastVersionPlayed}"); + Console.WriteLine($" Dummy 1: {Dummy1}"); + Console.WriteLine($" Dummy 2: {Dummy2}"); + Console.WriteLine($" File size: {FileSize}"); + Console.WriteLine($" Block size: {BlockSize}"); + Console.WriteLine($" Block count: {BlockCount}"); + Console.WriteLine($" Dummy 3: {Dummy3}"); + Console.WriteLine(); + } + + /// + /// Print block entry header information + /// + private void PrintBlockEntryHeader() + { + Console.WriteLine(" Block Entry Header Information:"); + Console.WriteLine(" -------------------------"); + Console.WriteLine($" Block count: {BEH_BlockCount}"); + Console.WriteLine($" Blocks used: {BEH_BlocksUsed}"); + Console.WriteLine($" Dummy 0: {BEH_Dummy0}"); + Console.WriteLine($" Dummy 1: {BEH_Dummy1}"); + Console.WriteLine($" Dummy 2: {BEH_Dummy2}"); + Console.WriteLine($" Dummy 3: {BEH_Dummy3}"); + Console.WriteLine($" Dummy 4: {BEH_Dummy4}"); + Console.WriteLine($" Checksum: {BEH_Checksum}"); + Console.WriteLine(); + } + + /// + /// Print block entries information + /// + private void PrintBlockEntries() + { + Console.WriteLine(" Block Entries Information:"); + Console.WriteLine(" -------------------------"); + if (BlockEntries == null || BlockEntries.Length == 0) + { + Console.WriteLine(" No block entries"); + } + else + { + for (int i = 0; i < BlockEntries.Length; i++) + { + var blockEntry = BlockEntries[i]; + Console.WriteLine($" Block Entry {i}"); + Console.WriteLine($" Entry flags: {blockEntry.EntryFlags}"); + Console.WriteLine($" File data offset: {blockEntry.FileDataOffset}"); + Console.WriteLine($" File data size: {blockEntry.FileDataSize}"); + Console.WriteLine($" First data block index: {blockEntry.FirstDataBlockIndex}"); + Console.WriteLine($" Next block entry index: {blockEntry.NextBlockEntryIndex}"); + Console.WriteLine($" Previous block entry index: {blockEntry.PreviousBlockEntryIndex}"); + Console.WriteLine($" Directory index: {blockEntry.DirectoryIndex}"); + } + } + Console.WriteLine(); + } + + /// + /// Print fragmentation map header information + /// + private void PrintFragmentationMapHeader() + { + Console.WriteLine(" Fragmentation Map Header Information:"); + Console.WriteLine(" -------------------------"); + Console.WriteLine($" Block count: {FMH_BlockCount}"); + Console.WriteLine($" First unused entry: {FMH_FirstUnusedEntry}"); + Console.WriteLine($" Terminator: {FMH_Terminator}"); + Console.WriteLine($" Checksum: {FMH_Checksum}"); + Console.WriteLine(); + } + + /// + /// Print fragmentation maps information + /// + private void PrintFragmentationMaps() + { + Console.WriteLine(" Fragmentation Maps Information:"); + Console.WriteLine(" -------------------------"); + if (FragmentationMaps == null || FragmentationMaps.Length == 0) + { + Console.WriteLine(" No fragmentation maps"); + } + else + { + for (int i = 0; i < FragmentationMaps.Length; i++) + { + var fragmentationMap = FragmentationMaps[i]; + Console.WriteLine($" Fragmentation Map {i}"); + Console.WriteLine($" Next data block index: {fragmentationMap.NextDataBlockIndex}"); + } + } + Console.WriteLine(); + } + + /// + /// Print block entry map header information + /// + private void PrintBlockEntryMapHeader() + { + Console.WriteLine(" Block Entry Map Header Information:"); + Console.WriteLine(" -------------------------"); + if (_file.BlockEntryMapHeader == null) + { + Console.WriteLine($" No block entry map header"); + } + else + { + Console.WriteLine($" Block count: {BEMH_BlockCount}"); + Console.WriteLine($" First block entry index: {BEMH_FirstBlockEntryIndex}"); + Console.WriteLine($" Last block entry index: {BEMH_LastBlockEntryIndex}"); + Console.WriteLine($" Dummy 0: {BEMH_Dummy0}"); + Console.WriteLine($" Checksum: {BEMH_Checksum}"); + } + Console.WriteLine(); + } + + /// + /// Print block entry maps information + /// + private void PrintBlockEntryMaps() + { + Console.WriteLine(" Block Entry Maps Information:"); + Console.WriteLine(" -------------------------"); + if (BlockEntryMaps == null || BlockEntryMaps.Length == 0) + { + Console.WriteLine(" No block entry maps"); + } + else + { + for (int i = 0; i < BlockEntryMaps.Length; i++) + { + var blockEntryMap = BlockEntryMaps[i]; + Console.WriteLine($" Block Entry Map {i}"); + Console.WriteLine($" Previous data block index: {blockEntryMap.PreviousBlockEntryIndex}"); + Console.WriteLine($" Next data block index: {blockEntryMap.NextBlockEntryIndex}"); + } + } + Console.WriteLine(); + } + + /// + /// Print directory header information + /// + private void PrintDirectoryHeader() + { + Console.WriteLine(" Directory Header Information:"); + Console.WriteLine(" -------------------------"); + Console.WriteLine($" Dummy 0: {DH_Dummy0}"); + Console.WriteLine($" Cache ID: {DH_CacheID}"); + Console.WriteLine($" Last version played: {DH_LastVersionPlayed}"); + Console.WriteLine($" Item count: {DH_ItemCount}"); + Console.WriteLine($" File count: {DH_FileCount}"); + Console.WriteLine($" Dummy 1: {DH_Dummy1}"); + Console.WriteLine($" Directory size: {DH_DirectorySize}"); + Console.WriteLine($" Name size: {DH_NameSize}"); + Console.WriteLine($" Info 1 count: {DH_Info1Count}"); + Console.WriteLine($" Copy count: {DH_CopyCount}"); + Console.WriteLine($" Local count: {DH_LocalCount}"); + Console.WriteLine($" Dummy 2: {DH_Dummy2}"); + Console.WriteLine($" Dummy 3: {DH_Dummy3}"); + Console.WriteLine($" Checksum: {DH_Checksum}"); + Console.WriteLine(); + } + + /// + /// Print directory entries information + /// + private void PrintDirectoryEntries() + { + Console.WriteLine(" Directory Entries Information:"); + Console.WriteLine(" -------------------------"); + if (DirectoryEntries == null || DirectoryEntries.Length == 0) + { + Console.WriteLine(" No directory entries"); + } + else + { + for (int i = 0; i < DirectoryEntries.Length; i++) + { + var directoryEntry = DirectoryEntries[i]; + Console.WriteLine($" Directory Entry {i}"); + Console.WriteLine($" Name offset: {directoryEntry.NameOffset}"); + Console.WriteLine($" Name: {directoryEntry.Name}"); + Console.WriteLine($" Item size: {directoryEntry.ItemSize}"); + Console.WriteLine($" Checksum index: {directoryEntry.ChecksumIndex}"); + Console.WriteLine($" Directory flags: {directoryEntry.DirectoryFlags}"); + Console.WriteLine($" Parent index: {directoryEntry.ParentIndex}"); + Console.WriteLine($" Next index: {directoryEntry.NextIndex}"); + Console.WriteLine($" First index: {directoryEntry.FirstIndex}"); + } + } + Console.WriteLine(); + } + + /// + /// Print directory info 1 entries information + /// + private void PrintDirectoryInfo1Entries() + { + Console.WriteLine(" Directory Info 1 Entries Information:"); + Console.WriteLine(" -------------------------"); + if (DirectoryInfo1Entries == null || DirectoryInfo1Entries.Length == 0) + { + Console.WriteLine(" No directory info 1 entries"); + } + else + { + for (int i = 0; i < DirectoryInfo1Entries.Length; i++) + { + var directoryInfoEntry = DirectoryInfo1Entries[i]; + Console.WriteLine($" Directory Info 1 Entry {i}"); + Console.WriteLine($" Dummy 0: {directoryInfoEntry.Dummy0}"); + } + } + Console.WriteLine(); + } + + /// + /// Print directory info 2 entries information + /// + private void PrintDirectoryInfo2Entries() + { + Console.WriteLine(" Directory Info 2 Entries Information:"); + Console.WriteLine(" -------------------------"); + if (DirectoryInfo2Entries == null || DirectoryInfo2Entries.Length == 0) + { + Console.WriteLine(" No directory info 2 entries"); + } + else + { + for (int i = 0; i < DirectoryInfo2Entries.Length; i++) + { + var directoryInfoEntry = DirectoryInfo2Entries[i]; + Console.WriteLine($" Directory Info 2 Entry {i}"); + Console.WriteLine($" Dummy 0: {directoryInfoEntry.Dummy0}"); + } + } + Console.WriteLine(); + } + + /// + /// Print directory copy entries information + /// + private void PrintDirectoryCopyEntries() + { + Console.WriteLine(" Directory Copy Entries Information:"); + Console.WriteLine(value: " -------------------------"); + if (DirectoryCopyEntries == null || DirectoryCopyEntries.Length == 0) + { + Console.WriteLine(" No directory copy entries"); + } + else + { + for (int i = 0; i < DirectoryCopyEntries.Length; i++) + { + var directoryCopyEntry = DirectoryCopyEntries[i]; + Console.WriteLine($" Directory Copy Entry {i}"); + Console.WriteLine($" Directory index: {directoryCopyEntry.DirectoryIndex}"); + } + } + Console.WriteLine(); + } + + /// + /// Print directory local entries information + /// + private void PrintDirectoryLocalEntries() + { + Console.WriteLine(" Directory Local Entries Information:"); + Console.WriteLine(value: " -------------------------"); + if (DirectoryLocalEntries == null || DirectoryLocalEntries.Length == 0) + { + Console.WriteLine(" No directory local entries"); + } + else + { + for (int i = 0; i < DirectoryLocalEntries.Length; i++) + { + var directoryLocalEntry = DirectoryLocalEntries[i]; + Console.WriteLine($" Directory Local Entry {i}"); + Console.WriteLine($" Directory index: {directoryLocalEntry.DirectoryIndex}"); + } + } + Console.WriteLine(); + } + + /// + /// Print directory map header information + /// + private void PrintDirectoryMapHeader() + { + Console.WriteLine(" Directory Map Header Information:"); + Console.WriteLine(" -------------------------"); + if (_file.DirectoryMapHeader == null) + { + Console.WriteLine($" No directory map header"); + } + else + { + Console.WriteLine($" Dummy 0: {DMH_Dummy0}"); + Console.WriteLine($" Dummy 1: {DMH_Dummy1}"); + } + Console.WriteLine(); + } + + /// + /// Print directory map entries information + /// + private void PrintDirectoryMapEntries() + { + Console.WriteLine(" Directory Map Entries Information:"); + Console.WriteLine(value: " -------------------------"); + if (DirectoryMapEntries == null || DirectoryMapEntries.Length == 0) + { + Console.WriteLine(" No directory map entries"); + } + else + { + for (int i = 0; i < DirectoryMapEntries.Length; i++) + { + var directoryMapEntry = DirectoryMapEntries[i]; + Console.WriteLine($" Directory Map Entry {i}"); + Console.WriteLine($" First block index: {directoryMapEntry.FirstBlockIndex}"); + } + } + Console.WriteLine(); + } + + /// + /// Print checksum header information + /// + private void PrintChecksumHeader() + { + Console.WriteLine(" Checksum Header Information:"); + Console.WriteLine(" -------------------------"); + Console.WriteLine($" Dummy 0: {CH_Dummy0}"); + Console.WriteLine($" Checksum size: {CH_ChecksumSize}"); + Console.WriteLine(); + } + + /// + /// Print checksum map header information + /// + private void PrintChecksumMapHeader() + { + Console.WriteLine(" Checksum Map Header Information:"); + Console.WriteLine(" -------------------------"); + Console.WriteLine($" Dummy 0: {CMH_Dummy0}"); + Console.WriteLine($" Dummy 1: {CMH_Dummy1}"); + Console.WriteLine($" Item count: {CMH_ItemCount}"); + Console.WriteLine($" Checksum count: {CMH_ChecksumCount}"); + Console.WriteLine(); + } + + /// + /// Print checksum map entries information + /// + private void PrintChecksumMapEntries() + { + Console.WriteLine(" Checksum Map Entries Information:"); + Console.WriteLine(value: " -------------------------"); + if (ChecksumMapEntries == null || ChecksumMapEntries.Length == 0) + { + Console.WriteLine(" No checksum map entries"); + } + else + { + for (int i = 0; i < ChecksumMapEntries.Length; i++) + { + var checksumMapEntry = ChecksumMapEntries[i]; + Console.WriteLine($" Checksum Map Entry {i}"); + Console.WriteLine($" Checksum count: {checksumMapEntry.ChecksumCount}"); + Console.WriteLine($" First checksum index: {checksumMapEntry.FirstChecksumIndex}"); + } + } + Console.WriteLine(); + } + + /// + /// Print checksum entries information + /// + private void PrintChecksumEntries() + { + Console.WriteLine(" Checksum Entries Information:"); + Console.WriteLine(value: " -------------------------"); + if (ChecksumEntries == null || ChecksumEntries.Length == 0) + { + Console.WriteLine(" No checksum entries"); + } + else + { + for (int i = 0; i < ChecksumEntries.Length; i++) + { + var checksumEntry = ChecksumEntries[i]; + Console.WriteLine($" Checksum Entry {i}"); + Console.WriteLine($" Checksum: {checksumEntry.Checksum}"); + } + } + Console.WriteLine(); + } + + /// + /// Print data block header information + /// + private void PrintDataBlockHeader() + { + Console.WriteLine(" Data Block Header Information:"); + Console.WriteLine(" -------------------------"); + Console.WriteLine($" Last version played: {DBH_LastVersionPlayed}"); + Console.WriteLine($" Block count: {DBH_BlockCount}"); + Console.WriteLine($" Block size: {DBH_BlockSize}"); + Console.WriteLine($" First block offset: {DBH_FirstBlockOffset}"); + Console.WriteLine($" Blocks used: {DBH_BlocksUsed}"); + Console.WriteLine($" Checksum: {DBH_Checksum}"); + Console.WriteLine(); + } + + #endregion + + #region Extraction + + /// + /// Extract all files from the GCF to an output directory + /// + /// Output directory to write to + /// True if all files extracted, false otherwise + public bool ExtractAll(string outputDirectory) + { + return false; + } + + /// + /// Extract a file from the GCF to an output directory by index + /// + /// File index to extract + /// Output directory to write to + /// True if the file extracted, false otherwise + public bool ExtractFile(int index, string outputDirectory) + { + return false; + } + + #endregion + } +} \ No newline at end of file diff --git a/BurnOutSharp.Wrappers/VPK.cs b/BurnOutSharp.Wrappers/VPK.cs index dd484e02..3b8568b4 100644 --- a/BurnOutSharp.Wrappers/VPK.cs +++ b/BurnOutSharp.Wrappers/VPK.cs @@ -126,11 +126,11 @@ namespace BurnOutSharp.Wrappers private VPK() { } /// - /// Create an VPK from a byte array and offset + /// Create a VPK from a byte array and offset /// /// Byte array representing the VPK /// Offset within the array to parse - /// An VPK wrapper on success, null on failure + /// A VPK wrapper on success, null on failure public static VPK Create(byte[] data, int offset) { // If the data is invalid @@ -147,10 +147,10 @@ namespace BurnOutSharp.Wrappers } /// - /// Create anVPK from a Stream + /// Create a VPK from a Stream /// - /// Stream representing the executable - /// An VPK wrapper on success, null on failure + /// Stream representing the VPK + /// A VPK wrapper on success, null on failure public static VPK Create(Stream data) { // If the data is invalid diff --git a/Test/Program.cs b/Test/Program.cs index 8db40c74..d72ae7dd 100644 --- a/Test/Program.cs +++ b/Test/Program.cs @@ -209,8 +209,8 @@ namespace Test { using (Stream stream = File.Open(file, FileMode.Open, FileAccess.Read, FileShare.Read)) { - // Read the first 4 bytes - byte[] magic = stream.ReadBytes(4); + // Read the first 8 bytes + byte[] magic = stream.ReadBytes(8); stream.Seek(0, SeekOrigin.Begin); // MS-DOS executable and decendents @@ -330,6 +330,25 @@ namespace Test bsp.Print(); } + // GCF + else if (IsGCF(magic)) + { + // Build the GCF information + Console.WriteLine("Creating GCF deserializer"); + Console.WriteLine(); + + var gcf = GCF.Create(stream); + if (gcf == null) + { + Console.WriteLine("Something went wrong parsing GCF"); + Console.WriteLine(); + return; + } + + // Print the GCF info to screen + gcf.Print(); + } + // MoPaQ (MPQ) archive else if (IsMoPaQ(magic)) { @@ -403,7 +422,7 @@ namespace Test } /// - /// Determine if the magic bytes indicate an BFPK archive + /// Determine if the magic bytes indicate a BSP /// private static bool IsBSP(byte[] magic) { @@ -413,6 +432,18 @@ namespace Test return magic[0] == 0x1e && magic[1] == 0x00 && magic[2] == 0x00 && magic[3] == 0x00; } + /// + /// Determine if the magic bytes indicate a GCF + /// + private static bool IsGCF(byte[] magic) + { + if (magic == null || magic.Length < 8) + return false; + + return magic[0] == 0x01 && magic[1] == 0x00 && magic[2] == 0x00 && magic[3] == 0x00 + && magic[4] == 0x01 && magic[5] == 0x00 && magic[6] == 0x00 && magic[7] == 0x00; + } + /// /// Determine if the magic bytes indicate an MoPaQ archive /// @@ -480,7 +511,7 @@ namespace Test } /// - /// Determine if the magic bytes indicate an VPK + /// Determine if the magic bytes indicate a VPK /// private static bool IsVPK(byte[] magic) {