From 598f625ed1552e4ac3130e136d35c5ec366ccca0 Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Thu, 7 Jul 2022 14:15:44 -0700 Subject: [PATCH] Add some deserialization for MPQ --- BurnOutSharp/FileType/MPQ.cs | 513 ++++++++++++++++++++++++++++------- 1 file changed, 411 insertions(+), 102 deletions(-) diff --git a/BurnOutSharp/FileType/MPQ.cs b/BurnOutSharp/FileType/MPQ.cs index 3a708ea0..d56ff36d 100644 --- a/BurnOutSharp/FileType/MPQ.cs +++ b/BurnOutSharp/FileType/MPQ.cs @@ -113,41 +113,6 @@ namespace BurnOutSharp.FileType /// internal class MoPaQArchive { - #region Constants - - #region Header Sizes - - public const int HeaderVersion1Size = 0x20; - - public const int HeaderVersion2Size = 0x2C; - - public const int HeaderVersion3Size = 0x44; - - public const int HeaderVersion4Size = 0xD0; - - #endregion - - #region Signatures - - /// - /// Human-readable signature - /// - public static readonly string SignatureString = $"MPQ{(char)0x1A}"; - - /// - /// Signature as an unsigned Int32 value - /// - public const uint SignatureValue = 0x1A51504D; - - /// - /// Signature as a byte array - /// - public static readonly byte[] SignatureBytes = new byte[] { 0x4D, 0x50, 0x51, 0x1A }; - - #endregion - - #endregion - #region Properties // Data before archive, ignored @@ -200,6 +165,59 @@ namespace BurnOutSharp.FileType // Strong digital signature #endregion + + #region Serialization + + /// + /// Deserialize at into a new MoPaQArchive object + /// + public static MoPaQArchive Deserialize(byte[] data, ref int dataPtr) + { + if (data == null || dataPtr < 0) + return null; + + MoPaQArchive archive = new MoPaQArchive(); + + // Check for User Data + uint possibleSignature = BitConverter.ToUInt32(data, dataPtr); + if (possibleSignature == MoPaQUserData.SignatureValue) + { + // Save the current position for offset correction + int basePtr = dataPtr; + + // Deserialize the user data, returning null if invalid + archive.UserData = MoPaQUserData.Deserialize(data, ref dataPtr); + if (archive.UserData == null) + return null; + + // Set the starting position according to the header offset + dataPtr = basePtr + (int)archive.UserData.HeaderOffset; + } + + // Check for the Header + possibleSignature = BitConverter.ToUInt32(data, dataPtr); + if (possibleSignature == MoPaQArchiveHeader.SignatureValue) + { + // Deserialize the header, returning null if invalid + archive.ArchiveHeader = MoPaQArchiveHeader.Deserialize(data, ref dataPtr); + if (archive.ArchiveHeader == null) + return null; + } + + // If we don't have a header, return null + if (archive.ArchiveHeader == null) + return null; + + // TODO: Read in Hash Table + // TODO: Read in Block Table + // TODO: Read in Hi-Block Table + // TODO: Read in BET Table + // TODO: Read in HET Table + + return archive; + } + + #endregion } /// @@ -241,21 +259,46 @@ namespace BurnOutSharp.FileType /// /// Maximum size of the user data /// - public int UserDataSize { get; private set; } + public uint UserDataSize { get; private set; } /// /// Offset of the MPQ header, relative to the beginning of this header /// - public int HeaderOffset { get; private set; } + public uint HeaderOffset { get; private set; } /// /// Appears to be size of user data header (Starcraft II maps) /// - public int UserDataHeadersize { get; private set; } + public uint UserDataHeaderSize { get; private set; } // TODO: Does this area contain extra data that should be read in? #endregion + + #region Serialization + + /// + /// Deserialize at into a new MoPaQUserData object + /// + public static MoPaQUserData Deserialize(byte[] data, ref int dataPtr) + { + if (data == null || dataPtr < 0) + return null; + + MoPaQUserData userData = new MoPaQUserData(); + + userData.Signature = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + if (userData.Signature != SignatureValue) + return null; + + userData.UserDataSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + userData.HeaderOffset = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + userData.UserDataHeaderSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + + return userData; + } + + #endregion } /// @@ -263,6 +306,41 @@ namespace BurnOutSharp.FileType /// internal class MoPaQArchiveHeader { + #region Constants + + #region Signatures + + /// + /// Human-readable signature + /// + public static readonly string SignatureString = $"MPQ{(char)0x1A}"; + + /// + /// Signature as an unsigned Int32 value + /// + public const uint SignatureValue = 0x1A51504D; + + /// + /// Signature as a byte array + /// + public static readonly byte[] SignatureBytes = new byte[] { 0x4D, 0x50, 0x51, 0x1A }; + + #endregion + + #region Header Sizes + + public const int HeaderVersion1Size = 0x20; + + public const int HeaderVersion2Size = 0x2C; + + public const int HeaderVersion3Size = 0x44; + + public const int HeaderVersion4Size = 0xD0; + + #endregion + + #endregion + #region V1 Properties /// @@ -273,7 +351,7 @@ namespace BurnOutSharp.FileType /// /// Size of the archive header /// - public int HeaderSize { get; private set; } + public uint HeaderSize { get; private set; } /// /// Size of MPQ archive @@ -283,7 +361,7 @@ namespace BurnOutSharp.FileType /// is calculated as the size from the beginning of the archive to the end of the hash table, /// block table, or extended block table (whichever is largest). /// - public int ArchiveSize { get; private set; } + public uint ArchiveSize { get; private set; } /// /// 0 = Format 1 (up to The Burning Crusade) @@ -291,34 +369,34 @@ namespace BurnOutSharp.FileType /// 2 = Format 3 (WoW - Cataclysm beta or newer) /// 3 = Format 4 (WoW - Cataclysm beta or newer) /// - public short FormatVersion { get; private set; } + public ushort FormatVersion { get; private set; } /// /// Power of two exponent specifying the number of 512-byte disk sectors in each logical sector /// in the archive. The size of each logical sector in the archive is 512 * 2 ^ BlockSize. /// - public short BlockSize { get; private set; } + public ushort BlockSize { get; private set; } /// /// Offset to the beginning of the hash table, relative to the beginning of the archive. /// - public int HashTablePosition { get; private set; } + public uint HashTablePosition { get; private set; } /// /// Offset to the beginning of the block table, relative to the beginning of the archive. /// - public int BlockTablePosition { get; private set; } + public uint BlockTablePosition { get; private set; } /// /// Number of entries in the hash table. Must be a power of two, and must be less than 2^16 for /// the original MoPaQ format, or less than 2^20 for the Burning Crusade format. /// - public int HashTableSize { get; private set; } + public uint HashTableSize { get; private set; } /// /// Number of entries in the block table /// - public int BlockTableSize { get; private set; } + public uint BlockTableSize { get; private set; } #endregion @@ -327,17 +405,17 @@ namespace BurnOutSharp.FileType /// /// Offset to the beginning of array of 16-bit high parts of file offsets. /// - public long HiBlockTablePosition { get; private set; } + public ulong HiBlockTablePosition { get; private set; } /// /// High 16 bits of the hash table offset for large archives. /// - public short HashTablePositionHi { get; private set; } + public ushort HashTablePositionHi { get; private set; } /// /// High 16 bits of the block table offset for large archives. /// - public short BlockTablePositionHi { get; private set; } + public ushort BlockTablePositionHi { get; private set; } #endregion @@ -346,17 +424,17 @@ namespace BurnOutSharp.FileType /// /// 64-bit version of the archive size /// - public long ArchiveSizeLong { get; private set; } + public ulong ArchiveSizeLong { get; private set; } /// /// 64-bit position of the BET table /// - public long BetTablePosition { get; private set; } + public ulong BetTablePosition { get; private set; } /// /// 64-bit position of the HET table /// - public long HetTablePosition { get; private set; } + public ulong HetTablePosition { get; private set; } #endregion @@ -365,35 +443,33 @@ namespace BurnOutSharp.FileType /// /// Compressed size of the hash table /// - public long HashTableSizeLong { get; private set; } + public ulong HashTableSizeLong { get; private set; } /// /// Compressed size of the block table /// - public long BlockTableSizeLong { get; private set; } + public ulong BlockTableSizeLong { get; private set; } /// /// Compressed size of the hi-block table /// - public long HiBlockTableSize { get; private set; } + public ulong HiBlockTableSize { get; private set; } /// /// Compressed size of the HET block /// - public long HetTableSize { get; private set; } + public ulong HetTableSize { get; private set; } /// /// Compressed size of the BET block /// - public long BetTablesize { get; private set; } + public ulong BetTablesize { get; private set; } /// /// Size of raw data chunk to calculate MD5. /// /// MD5 of each data chunk follows the raw file data. - public int RawChunkSize { get; private set; } - - // TODO: Is there a byte[] here of size RawChunkSize? + public uint RawChunkSize { get; private set; } /// /// MD5 of the block table before decryption @@ -426,6 +502,71 @@ namespace BurnOutSharp.FileType public byte[] MpqHeaderMD5 { get; private set; } = new byte[0x10]; #endregion + + #region Serialization + + /// + /// Deserialize at into a new MoPaQArchiveHeader object + /// + public static MoPaQArchiveHeader Deserialize(byte[] data, ref int dataPtr) + { + if (data == null || dataPtr < 0) + return null; + + MoPaQArchiveHeader archiveHeader = new MoPaQArchiveHeader(); + + // V1 - Common + archiveHeader.Signature = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + if (archiveHeader.Signature != SignatureValue) + return null; + + archiveHeader.HeaderSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + archiveHeader.ArchiveSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + archiveHeader.FormatVersion = BitConverter.ToUInt16(data, dataPtr); dataPtr += 2; + archiveHeader.BlockSize = BitConverter.ToUInt16(data, dataPtr); dataPtr += 2; + archiveHeader.HashTablePosition = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + archiveHeader.BlockTablePosition = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + archiveHeader.HashTableSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + archiveHeader.BlockTableSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + + // V2 + if (archiveHeader.FormatVersion >= 2 && archiveHeader.HeaderSize >= HeaderVersion2Size) + { + archiveHeader.HiBlockTablePosition = BitConverter.ToUInt64(data, dataPtr); dataPtr += 8; + archiveHeader.HashTablePositionHi = BitConverter.ToUInt16(data, dataPtr); dataPtr += 2; + archiveHeader.BlockTablePositionHi = BitConverter.ToUInt16(data, dataPtr); dataPtr += 2; + } + + // V3 + if (archiveHeader.FormatVersion >= 3 && archiveHeader.HeaderSize >= HeaderVersion3Size) + { + archiveHeader.ArchiveSizeLong = BitConverter.ToUInt64(data, dataPtr); dataPtr += 8; + archiveHeader.BetTablePosition = BitConverter.ToUInt64(data, dataPtr); dataPtr += 8; + archiveHeader.HetTablePosition = BitConverter.ToUInt64(data, dataPtr); dataPtr += 8; + } + + // V4 + if (archiveHeader.FormatVersion >= 4 && archiveHeader.HeaderSize >= HeaderVersion4Size) + { + archiveHeader.HashTableSizeLong = BitConverter.ToUInt64(data, dataPtr); dataPtr += 8; + archiveHeader.BlockTableSizeLong = BitConverter.ToUInt64(data, dataPtr); dataPtr += 8; + archiveHeader.HiBlockTableSize = BitConverter.ToUInt64(data, dataPtr); dataPtr += 8; + archiveHeader.HetTableSize = BitConverter.ToUInt64(data, dataPtr); dataPtr += 8; + archiveHeader.BetTablesize = BitConverter.ToUInt64(data, dataPtr); dataPtr += 8; + archiveHeader.RawChunkSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + + Array.Copy(data, dataPtr, archiveHeader.BlockTableMD5, 0, 0x10); dataPtr += 0x10; + Array.Copy(data, dataPtr, archiveHeader.HashTableMD5, 0, 0x10); dataPtr += 0x10; + Array.Copy(data, dataPtr, archiveHeader.HiBlockTableMD5, 0, 0x10); dataPtr += 0x10; + Array.Copy(data, dataPtr, archiveHeader.BetTableMD5, 0, 0x10); dataPtr += 0x10; + Array.Copy(data, dataPtr, archiveHeader.HetTableMD5, 0, 0x10); dataPtr += 0x10; + Array.Copy(data, dataPtr, archiveHeader.MpqHeaderMD5, 0, 0x10); dataPtr += 0x10; + } + + return archiveHeader; + } + + #endregion } /// @@ -468,12 +609,12 @@ namespace BurnOutSharp.FileType /// /// Version. Seems to be always 1 /// - public int Version { get; private set; } + public uint Version { get; private set; } /// /// Size of the contained table /// - public int DataSize { get; private set; } + public uint DataSize { get; private set; } #endregion @@ -482,42 +623,42 @@ namespace BurnOutSharp.FileType /// /// Size of the entire hash table, including the header (in bytes) /// - public int TableSize { get; private set; } + public uint TableSize { get; private set; } /// /// Maximum number of files in the MPQ /// - public int MaxFileCount { get; private set; } + public uint MaxFileCount { get; private set; } /// /// Size of the hash table (in bytes) /// - public int HashTableSize { get; private set; } + public uint HashTableSize { get; private set; } /// /// Effective size of the hash entry (in bits) /// - public int HashEntrySize { get; private set; } + public uint HashEntrySize { get; private set; } /// /// Total size of file index (in bits) /// - public int TotalIndexSize { get; private set; } + public uint TotalIndexSize { get; private set; } /// /// Extra bits in the file index /// - public int IndexSizeExtra { get; private set; } + public uint IndexSizeExtra { get; private set; } /// /// Effective size of the file index (in bits) /// - public int IndexSize { get; private set; } + public uint IndexSize { get; private set; } /// /// Size of the block index subtable (in bytes) /// - public int BlockTableSize { get; private set; } + public uint BlockTableSize { get; private set; } /// /// HET hash table. Each entry is 8 bits. @@ -525,9 +666,52 @@ namespace BurnOutSharp.FileType /// Size is derived from HashTableSize public byte[] HashTable { get; private set; } - // TODO: Implement both of these on parse - // Array of file indexes. Bit size of each entry is taken from dwTotalIndexSize. - // Table size is taken from dwHashTableSize. + /// + /// Array of file indexes. Bit size of each entry is taken from dwTotalIndexSize. + /// Table size is taken from dwHashTableSize. + /// + public byte[][] FileIndexes { get; private set; } + + #endregion + + #region Serialization + + /// + /// Deserialize at into a new MoPaQHetTable object + /// + public static MoPaQHetTable Deserialize(byte[] data, ref int dataPtr) + { + if (data == null || dataPtr < 0) + return null; + + MoPaQHetTable hetTable = new MoPaQHetTable(); + + // Common Headers + hetTable.Signature = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + if (hetTable.Signature != SignatureValue) + return null; + + hetTable.Version = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + hetTable.DataSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + + // HET-Specific + hetTable.TableSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + hetTable.MaxFileCount = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + hetTable.HashTableSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + hetTable.TotalIndexSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + hetTable.IndexSizeExtra = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + hetTable.IndexSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + hetTable.BlockTableSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + + hetTable.HashTable = new byte[hetTable.HashTableSize]; + Array.Copy(data, dataPtr, hetTable.HashTable, 0, hetTable.HashTableSize); + dataPtr += (int)hetTable.HashTableSize; + + // TODO: Populate the file indexes array + hetTable.FileIndexes = new byte[(int)hetTable.HashTableSize][]; + + return hetTable; + } #endregion } @@ -571,12 +755,12 @@ namespace BurnOutSharp.FileType /// /// Version. Seems to be always 1 /// - public int Version { get; private set; } + public uint Version { get; private set; } /// /// Size of the contained table /// - public int DataSize { get; private set; } + public uint DataSize { get; private set; } #endregion @@ -585,103 +769,103 @@ namespace BurnOutSharp.FileType /// /// Size of the entire hash table, including the header (in bytes) /// - public int TableSize { get; private set; } + public uint TableSize { get; private set; } /// /// Number of files in the BET table /// - public int FileCount { get; private set; } + public uint FileCount { get; private set; } /// /// Unknown, set to 0x10 /// - public int Unknown { get; private set; } + public uint Unknown { get; private set; } /// /// Size of one table entry (in bits) /// - public int TableEntrySize { get; private set; } + public uint TableEntrySize { get; private set; } /// /// Bit index of the file position (within the entry record) /// - public int FilePositionBitIndex { get; private set; } + public uint FilePositionBitIndex { get; private set; } /// /// Bit index of the file size (within the entry record) /// - public int FileSizeBitIndex { get; private set; } + public uint FileSizeBitIndex { get; private set; } /// /// Bit index of the compressed size (within the entry record) /// - public int CompressedSizeBitIndex { get; private set; } + public uint CompressedSizeBitIndex { get; private set; } /// /// Bit index of the flag index (within the entry record) /// - public int FlagIndexBitIndex { get; private set; } + public uint FlagIndexBitIndex { get; private set; } /// /// Bit index of the ??? (within the entry record) /// - public int UnknownBitIndex { get; private set; } + public uint UnknownBitIndex { get; private set; } /// /// Bit size of file position (in the entry record) /// - public int FilePositionBitCount { get; private set; } + public uint FilePositionBitCount { get; private set; } /// /// Bit size of file size (in the entry record) /// - public int FileSizeBitCount { get; private set; } + public uint FileSizeBitCount { get; private set; } /// /// Bit size of compressed file size (in the entry record) /// - public int CompressedSizeBitCount { get; private set; } + public uint CompressedSizeBitCount { get; private set; } /// /// Bit size of flags index (in the entry record) /// - public int FlagIndexBitCount { get; private set; } + public uint FlagIndexBitCount { get; private set; } /// /// Bit size of ??? (in the entry record) /// - public int UnknownBitCount { get; private set; } + public uint UnknownBitCount { get; private set; } /// /// Total size of the BET hash /// - public int TotalBetHashSize { get; private set; } + public uint TotalBetHashSize { get; private set; } /// /// Extra bits in the BET hash /// - public int BetHashSizeExtra { get; private set; } + public uint BetHashSizeExtra { get; private set; } /// /// Effective size of BET hash (in bits) /// - public int BetHashSize { get; private set; } + public uint BetHashSize { get; private set; } /// /// Size of BET hashes array, in bytes /// - public int BetHashArraySize { get; private set; } + public uint BetHashArraySize { get; private set; } /// /// Number of flags in the following array /// - public int FlagCount { get; private set; } + public uint FlagCount { get; private set; } /// /// Followed by array of file flags. Each entry is 32-bit size and its meaning is the same like /// /// Size from - public int[] FlagsArray { get; private set; } + public uint[] FlagsArray { get; private set; } // File table. Size of each entry is taken from dwTableEntrySize. // Size of the table is (dwTableEntrySize * dwMaxFileCount), round up to 8. @@ -689,6 +873,62 @@ namespace BurnOutSharp.FileType // Array of BET hashes. Table size is taken from dwMaxFileCount from HET table #endregion + + #region Serialization + + /// + /// Deserialize at into a new MoPaQBetTable object + /// + public static MoPaQBetTable Deserialize(byte[] data, ref int dataPtr) + { + if (data == null || dataPtr < 0) + return null; + + MoPaQBetTable betTable = new MoPaQBetTable(); + + // Common Headers + betTable.Signature = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + if (betTable.Signature != SignatureValue) + return null; + + betTable.Version = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + betTable.DataSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + + // BET-Specific + betTable.TableSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + betTable.FileCount = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + betTable.Unknown = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + betTable.TableEntrySize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + + betTable.FilePositionBitIndex = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + betTable.FileSizeBitIndex = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + betTable.CompressedSizeBitIndex = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + betTable.FlagIndexBitIndex = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + betTable.UnknownBitIndex = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + + betTable.FilePositionBitCount = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + betTable.FileSizeBitCount = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + betTable.CompressedSizeBitCount = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + betTable.FlagIndexBitCount = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + betTable.UnknownBitCount = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + + betTable.TotalBetHashSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + betTable.BetHashSizeExtra = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + betTable.BetHashSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + betTable.BetHashArraySize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + betTable.FlagCount = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + + betTable.FlagsArray = new uint[betTable.FlagCount]; + Buffer.BlockCopy(data, dataPtr, betTable.FlagsArray, 0, (int)betTable.FlagCount * 4); + dataPtr += (int)betTable.FlagCount * 4; + + // TODO: Populate the file table + // TODO: Populate the hash table + + return betTable; + } + + #endregion } /// @@ -728,7 +968,7 @@ namespace BurnOutSharp.FileType /// The platform the file is used for. 0 indicates the default platform. /// No other values have been observed. /// - public short Platform { get; private set; } + public ushort Platform { get; private set; } /// /// If the hash table entry is valid, this is the index into the block table of the file. @@ -741,6 +981,29 @@ namespace BurnOutSharp.FileType public uint BlockIndex { get; private set; } #endregion + + #region Serialization + + /// + /// Deserialize at into a new MoPaQHashEntry object + /// + public static MoPaQHashEntry Deserialize(byte[] data, ref int dataPtr) + { + if (data == null || dataPtr < 0) + return null; + + MoPaQHashEntry hashEntry = new MoPaQHashEntry(); + + hashEntry.NameHashPartA = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + hashEntry.NameHashPartB = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + hashEntry.Locale = (MoPaQLocale)BitConverter.ToUInt16(data, dataPtr); dataPtr += 2; + hashEntry.Platform = BitConverter.ToUInt16(data, dataPtr); dataPtr += 2; + hashEntry.BlockIndex = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + + return hashEntry; + } + + #endregion } internal enum MoPaQLocale : short @@ -780,17 +1043,17 @@ namespace BurnOutSharp.FileType /// /// Offset of the beginning of the file data, relative to the beginning of the archive. /// - public int FilePosition { get; private set; } + public uint FilePosition { get; private set; } /// /// Compressed file size /// - public int CompressedSize { get; private set; } + public uint CompressedSize { get; private set; } /// /// Size of uncompressed file /// - public int UncompressedSize { get; private set; } + public uint UncompressedSize { get; private set; } /// /// Flags for the file. @@ -798,6 +1061,28 @@ namespace BurnOutSharp.FileType public MoPaQFileFlags Flags { get; private set; } #endregion + + #region Serialization + + /// + /// Deserialize at into a new MoPaQBlockEntry object + /// + public static MoPaQBlockEntry Deserialize(byte[] data, ref int dataPtr) + { + if (data == null || dataPtr < 0) + return null; + + MoPaQBlockEntry blockEntry = new MoPaQBlockEntry(); + + blockEntry.FilePosition = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + blockEntry.CompressedSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + blockEntry.UncompressedSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + blockEntry.Flags = (MoPaQFileFlags)BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + + return blockEntry; + } + + #endregion } [Flags] @@ -865,7 +1150,7 @@ namespace BurnOutSharp.FileType /// /// Length of patch info header, in bytes /// - public int Length { get; private set; } + public uint Length { get; private set; } /// /// Flags. 0x80000000 = MD5 (?) @@ -875,7 +1160,7 @@ namespace BurnOutSharp.FileType /// /// Uncompressed size of the patch file /// - public int DataSize { get; private set; } + public uint DataSize { get; private set; } /// /// MD5 of the entire patch file after decompression @@ -885,7 +1170,31 @@ namespace BurnOutSharp.FileType /// /// The sector offset table (variable length) /// - public int[] SectorOffsetTable { get; private set; } + public uint[] SectorOffsetTable { get; private set; } + + #endregion + + #region Serialization + + /// + /// Deserialize at into a new MoPaQPatchInfo object + /// + public static MoPaQPatchInfo Deserialize(byte[] data, ref int dataPtr) + { + if (data == null || dataPtr < 0) + return null; + + MoPaQPatchInfo patchInfo = new MoPaQPatchInfo(); + + patchInfo.Length = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + patchInfo.Flags = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + patchInfo.DataSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4; + Array.Copy(data, dataPtr, patchInfo.MD5, 0, 0x10); dataPtr += 0x10; + + // TODO: Fill the sector offset table + + return patchInfo; + } #endregion }