diff --git a/BurnOutSharp.Models/MoPaQ/Archive.cs b/BurnOutSharp.Models/MoPaQ/Archive.cs
new file mode 100644
index 00000000..08a6bb65
--- /dev/null
+++ b/BurnOutSharp.Models/MoPaQ/Archive.cs
@@ -0,0 +1,61 @@
+namespace BurnOutSharp.Models.MoPaQ
+{
+ ///
+ /// MPQ (MoPaQ) is an archive format developed by Blizzard Entertainment,
+ /// purposed for storing data files, images, sounds, music and videos for
+ /// their games. The name MoPaQ comes from the author of the format,
+ /// Mike O'Brien (Mike O'brien PaCK).
+ ///
+ ///
+ public class Archive
+ {
+ // TODO: Data before archive, ignored
+
+ ///
+ /// MPQ User Data (optional)
+ ///
+ public UserData UserData { get; set; }
+
+ ///
+ /// MPQ Header (required)
+ ///
+ public ArchiveHeader ArchiveHeader { get; set; }
+
+ // TODO: Files (optional)
+ // TODO: Special files (optional)
+
+ ///
+ /// HET Table (optional)
+ ///
+ public HetTable HetTable { get; set; }
+
+ ///
+ /// BET Table (optional)
+ ///
+ public BetTable BetTable { get; set; }
+
+ ///
+ /// Hash Table (optional)
+ ///
+ public HashEntry[] HashTable { get; set; }
+
+ ///
+ /// Block Table (optional)
+ ///
+ public BlockEntry[] BlockTable { get; set; }
+
+ ///
+ /// Hi-Block Table (optional)
+ ///
+ ///
+ /// Since World of Warcraft - The Burning Crusade, Blizzard extended
+ /// the MPQ format to support archives larger than 4GB. The hi-block
+ /// table holds the higher 16-bits of the file position in the MPQ.
+ /// Hi-block table is plain array of 16-bit values. This table is
+ /// not encrypted.
+ ///
+ public short[] HiBlockTable { get; set; }
+
+ // TODO: Strong digital signature
+ }
+}
diff --git a/BurnOutSharp.Models/MoPaQ/ArchiveHeader.cs b/BurnOutSharp.Models/MoPaQ/ArchiveHeader.cs
new file mode 100644
index 00000000..4a1fe308
--- /dev/null
+++ b/BurnOutSharp.Models/MoPaQ/ArchiveHeader.cs
@@ -0,0 +1,180 @@
+using System.Runtime.InteropServices;
+
+namespace BurnOutSharp.Models.MoPaQ
+{
+ ///
+ /// MoPaQ archive header
+ ///
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public class ArchiveHeader
+ {
+ #region V1 Properties
+
+ ///
+ /// The MPQ archive signature
+ ///
+ public uint Signature;
+
+ ///
+ /// Size of the archive header
+ ///
+ public uint HeaderSize;
+
+ ///
+ /// Size of MPQ archive
+ ///
+ ///
+ /// This field is deprecated in the Burning Crusade MoPaQ format, and the size of the archive
+ /// 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 uint ArchiveSize;
+
+ ///
+ /// 0 = Format 1 (up to The Burning Crusade)
+ /// 1 = Format 2 (The Burning Crusade and newer)
+ /// 2 = Format 3 (WoW - Cataclysm beta or newer)
+ /// 3 = Format 4 (WoW - Cataclysm beta or newer)
+ ///
+ public ushort FormatVersion;
+
+ ///
+ /// 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 ushort BlockSize;
+
+ ///
+ /// Offset to the beginning of the hash table, relative to the beginning of the archive.
+ ///
+ public uint HashTablePosition;
+
+ ///
+ /// Offset to the beginning of the block table, relative to the beginning of the archive.
+ ///
+ public uint BlockTablePosition;
+
+ ///
+ /// 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 uint HashTableSize;
+
+ ///
+ /// Number of entries in the block table
+ ///
+ public uint BlockTableSize;
+
+ #endregion
+
+ #region V2 Properties
+
+ ///
+ /// Offset to the beginning of array of 16-bit high parts of file offsets.
+ ///
+ public ulong HiBlockTablePosition;
+
+ ///
+ /// High 16 bits of the hash table offset for large archives.
+ ///
+ public ushort HashTablePositionHi;
+
+ ///
+ /// High 16 bits of the block table offset for large archives.
+ ///
+ public ushort BlockTablePositionHi;
+
+ #endregion
+
+ #region V3 Properties
+
+ ///
+ /// 64-bit version of the archive size
+ ///
+ public ulong ArchiveSizeLong;
+
+ ///
+ /// 64-bit position of the BET table
+ ///
+ public ulong BetTablePosition;
+
+ ///
+ /// 64-bit position of the HET table
+ ///
+ public ulong HetTablePosition;
+
+ #endregion
+
+ #region V4 Properties
+
+ ///
+ /// Compressed size of the hash table
+ ///
+ public ulong HashTableSizeLong;
+
+ ///
+ /// Compressed size of the block table
+ ///
+ public ulong BlockTableSizeLong;
+
+ ///
+ /// Compressed size of the hi-block table
+ ///
+ public ulong HiBlockTableSize;
+
+ ///
+ /// Compressed size of the HET block
+ ///
+ public ulong HetTableSize;
+
+ ///
+ /// Compressed size of the BET block
+ ///
+ public ulong BetTablesize;
+
+ ///
+ /// Size of raw data chunk to calculate MD5.
+ ///
+ /// MD5 of each data chunk follows the raw file data.
+ public uint RawChunkSize;
+
+ ///
+ /// MD5 of the block table before decryption
+ ///
+ /// 0x10 bytes
+ public byte[] BlockTableMD5;
+
+ ///
+ /// MD5 of the hash table before decryption
+ ///
+ /// 0x10 bytes
+ public byte[] HashTableMD5;
+
+ ///
+ /// MD5 of the hi-block table
+ ///
+ /// 0x10 bytes
+ public byte[] HiBlockTableMD5;
+
+ ///
+ /// MD5 of the BET table before decryption
+ ///
+ /// 0x10 bytes
+ public byte[] BetTableMD5;
+
+ ///
+ /// MD5 of the HET table before decryption
+ ///
+ /// 0x10 bytes
+ public byte[] HetTableMD5;
+
+ ///
+ /// MD5 of the MPQ header from signature to (including) HetTableMD5
+ ///
+ /// 0x10 bytes
+ public byte[] MpqHeaderMD5;
+
+ #endregion
+ }
+}
diff --git a/BurnOutSharp.Models/MoPaQ/BetTable.cs b/BurnOutSharp.Models/MoPaQ/BetTable.cs
new file mode 100644
index 00000000..4b6d7bfb
--- /dev/null
+++ b/BurnOutSharp.Models/MoPaQ/BetTable.cs
@@ -0,0 +1,140 @@
+using System.Runtime.InteropServices;
+
+namespace BurnOutSharp.Models.MoPaQ
+{
+ ///
+ /// The BET table is present if the BetTablePos64 member of MPQ header is set
+ /// to nonzero. BET table is a successor of classic block table, and can fully
+ /// replace it. It is also supposed to be more effective.
+ ///
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public class BetTable
+ {
+ // TODO: Extract this out and make in common between HET and BET
+ #region Common Table Headers
+
+ ///
+ /// 'BET\x1A'
+ ///
+ public uint Signature;
+
+ ///
+ /// Version. Seems to be always 1
+ ///
+ public uint Version;
+
+ ///
+ /// Size of the contained table
+ ///
+ public uint DataSize;
+
+ #endregion
+
+ ///
+ /// Size of the entire hash table, including the header (in bytes)
+ ///
+ public uint TableSize;
+
+ ///
+ /// Number of files in the BET table
+ ///
+ public uint FileCount;
+
+ ///
+ /// Unknown, set to 0x10
+ ///
+ public uint Unknown;
+
+ ///
+ /// Size of one table entry (in bits)
+ ///
+ public uint TableEntrySize;
+
+ ///
+ /// Bit index of the file position (within the entry record)
+ ///
+ public uint FilePositionBitIndex;
+
+ ///
+ /// Bit index of the file size (within the entry record)
+ ///
+ public uint FileSizeBitIndex;
+
+ ///
+ /// Bit index of the compressed size (within the entry record)
+ ///
+ public uint CompressedSizeBitIndex;
+
+ ///
+ /// Bit index of the flag index (within the entry record)
+ ///
+ public uint FlagIndexBitIndex;
+
+ ///
+ /// Bit index of the ??? (within the entry record)
+ ///
+ public uint UnknownBitIndex;
+
+ ///
+ /// Bit size of file position (in the entry record)
+ ///
+ public uint FilePositionBitCount;
+
+ ///
+ /// Bit size of file size (in the entry record)
+ ///
+ public uint FileSizeBitCount;
+
+ ///
+ /// Bit size of compressed file size (in the entry record)
+ ///
+ public uint CompressedSizeBitCount;
+
+ ///
+ /// Bit size of flags index (in the entry record)
+ ///
+ public uint FlagIndexBitCount;
+
+ ///
+ /// Bit size of ??? (in the entry record)
+ ///
+ public uint UnknownBitCount;
+
+ ///
+ /// Total size of the BET hash
+ ///
+ public uint TotalBetHashSize;
+
+ ///
+ /// Extra bits in the BET hash
+ ///
+ public uint BetHashSizeExtra;
+
+ ///
+ /// Effective size of BET hash (in bits)
+ ///
+ public uint BetHashSize;
+
+ ///
+ /// Size of BET hashes array, in bytes
+ ///
+ public uint BetHashArraySize;
+
+ ///
+ /// Number of flags in the following array
+ ///
+ public uint FlagCount;
+
+ ///
+ /// Followed by array of file flags. Each entry is 32-bit size and its meaning is the same like
+ ///
+ /// Size from
+ public uint[] FlagsArray;
+
+ // File table. Size of each entry is taken from dwTableEntrySize.
+ // Size of the table is (dwTableEntrySize * dwMaxFileCount), round up to 8.
+
+ // Array of BET hashes. Table size is taken from dwMaxFileCount from HET table
+ }
+}
diff --git a/BurnOutSharp.Models/MoPaQ/BlockEntry.cs b/BurnOutSharp.Models/MoPaQ/BlockEntry.cs
new file mode 100644
index 00000000..ff769260
--- /dev/null
+++ b/BurnOutSharp.Models/MoPaQ/BlockEntry.cs
@@ -0,0 +1,34 @@
+using System.Runtime.InteropServices;
+
+namespace BurnOutSharp.Models.MoPaQ
+{
+ ///
+ /// Block table contains informations about file sizes and way of their storage within
+ /// the archive. It also contains the position of file content in the archive. Size
+ /// of block table entry is (like hash table entry). The block table is also encrypted.
+ ///
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public class BlockEntry
+ {
+ ///
+ /// Offset of the beginning of the file data, relative to the beginning of the archive.
+ ///
+ public uint FilePosition;
+
+ ///
+ /// Compressed file size
+ ///
+ public uint CompressedSize;
+
+ ///
+ /// Size of uncompressed file
+ ///
+ public uint UncompressedSize;
+
+ ///
+ /// Flags for the file.
+ ///
+ public MoPaQFileFlags Flags;
+ }
+}
diff --git a/BurnOutSharp.Models/MoPaQ/Enums.cs b/BurnOutSharp.Models/MoPaQ/Enums.cs
new file mode 100644
index 00000000..3ccf683e
--- /dev/null
+++ b/BurnOutSharp.Models/MoPaQ/Enums.cs
@@ -0,0 +1,106 @@
+using System;
+
+namespace BurnOutSharp.Models.MoPaQ
+{
+ [Flags]
+ public enum MoPaQFileFlags : uint
+ {
+ ///
+ /// File is compressed using PKWARE Data compression library
+ ///
+ MPQ_FILE_IMPLODE = 0x00000100,
+
+ ///
+ /// File is compressed using combination of compression methods
+ ///
+ MPQ_FILE_COMPRESS = 0x00000200,
+
+ ///
+ /// The file is encrypted
+ ///
+ MPQ_FILE_ENCRYPTED = 0x00010000,
+
+ ///
+ /// The decryption key for the file is altered according to the
+ /// position of the file in the archive
+ ///
+ MPQ_FILE_FIX_KEY = 0x00020000,
+
+ ///
+ /// The file contains incremental patch for an existing file in base MPQ
+ ///
+ MPQ_FILE_PATCH_FILE = 0x00100000,
+
+ ///
+ /// Instead of being divided to 0x1000-bytes blocks, the file is stored
+ /// as single unit
+ ///
+ MPQ_FILE_SINGLE_UNIT = 0x01000000,
+
+ ///
+ /// File is a deletion marker, indicating that the file no longer exists.
+ /// This is used to allow patch archives to delete files present in
+ /// lower-priority archives in the search chain. The file usually has
+ /// length of 0 or 1 byte and its name is a hash
+ ///
+ MPQ_FILE_DELETE_MARKER = 0x02000000,
+
+ ///
+ /// File has checksums for each sector (explained in the File Data section).
+ /// Ignored if file is not compressed or imploded.
+ ///
+ MPQ_FILE_SECTOR_CRC = 0x04000000,
+
+ ///
+ /// Set if file exists, reset when the file was deleted
+ ///
+ MPQ_FILE_EXISTS = 0x80000000,
+ }
+
+ public enum Locale : short
+ {
+ Neutral = 0,
+ AmericanEnglish = 0,
+ ChineseTaiwan = 0x404,
+ Czech = 0x405,
+ German = 0x407,
+ English = 0x409,
+ Spanish = 0x40A,
+ French = 0x40C,
+ Italian = 0x410,
+ Japanese = 0x411,
+ Korean = 0x412,
+ Polish = 0x415,
+ Portuguese = 0x416,
+ Russian = 0x419,
+ EnglishUK = 0x809,
+ }
+
+ public enum MoPaQPatchType : uint
+ {
+ ///
+ /// Blizzard-modified version of BSDIFF40 incremental patch
+ ///
+ BSD0 = 0x30445342,
+
+ ///
+ /// Unknown
+ ///
+ BSDP = 0x50445342,
+
+ ///
+ /// Plain replace
+ ///
+ COPY = 0x59504F43,
+
+ ///
+ /// Unknown
+ ///
+ COUP = 0x50554F43,
+
+ ///
+ /// Unknown
+ ///
+ CPOG = 0x474F5043,
+ }
+}
diff --git a/BurnOutSharp.Models/MoPaQ/HashEntry.cs b/BurnOutSharp.Models/MoPaQ/HashEntry.cs
new file mode 100644
index 00000000..211cd1ec
--- /dev/null
+++ b/BurnOutSharp.Models/MoPaQ/HashEntry.cs
@@ -0,0 +1,48 @@
+using System.Runtime.InteropServices;
+
+namespace BurnOutSharp.Models.MoPaQ
+{
+ ///
+ /// Hash table is used for searching files by name. The file name is converted to
+ /// two 32-bit hash values, which are then used for searching in the table. The size
+ /// of the hash table must always be a power of two. Each entry in the hash table
+ /// also contains file locale and offset into block table. Size of one entry of hash
+ /// table is 16 bytes.
+ ///
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public class HashEntry
+ {
+ ///
+ /// The hash of the full file name (part A)
+ ///
+ public uint NameHashPartA;
+
+ ///
+ /// The hash of the full file name (part B)
+ ///
+ public uint NameHashPartB;
+
+ ///
+ /// The language of the file. This is a Windows LANGID data type, and uses the same values.
+ /// 0 indicates the default language (American English), or that the file is language-neutral.
+ ///
+ public Locale Locale;
+
+ ///
+ /// The platform the file is used for. 0 indicates the default platform.
+ /// No other values have been observed.
+ ///
+ public ushort Platform;
+
+ ///
+ /// If the hash table entry is valid, this is the index into the block table of the file.
+ /// Otherwise, one of the following two values:
+ /// - FFFFFFFFh: Hash table entry is empty, and has always been empty.
+ /// Terminates searches for a given file.
+ /// - FFFFFFFEh: Hash table entry is empty, but was valid at some point (a deleted file).
+ /// Does not terminate searches for a given file.
+ ///
+ public uint BlockIndex;
+ }
+}
diff --git a/BurnOutSharp.Models/MoPaQ/HetTable.cs b/BurnOutSharp.Models/MoPaQ/HetTable.cs
new file mode 100644
index 00000000..07c6b879
--- /dev/null
+++ b/BurnOutSharp.Models/MoPaQ/HetTable.cs
@@ -0,0 +1,87 @@
+using System.Runtime.InteropServices;
+
+namespace BurnOutSharp.Models.MoPaQ
+{
+ ///
+ /// The HET table is present if the HetTablePos64 member of MPQ header is
+ /// set to nonzero. This table can fully replace hash table. Depending on
+ /// MPQ size, the pair of HET&BET table can be more efficient than Hash&Block
+ /// table. HET table can be encrypted and compressed.
+ ///
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public class HetTable
+ {
+ // TODO: Extract this out and make in common between HET and BET
+ #region Common Table Headers
+
+ ///
+ /// 'HET\x1A'
+ ///
+ public uint Signature;
+
+ ///
+ /// Version. Seems to be always 1
+ ///
+ public uint Version;
+
+ ///
+ /// Size of the contained table
+ ///
+ public uint DataSize;
+
+ #endregion
+
+ ///
+ /// Size of the entire hash table, including the header (in bytes)
+ ///
+ public uint TableSize;
+
+ ///
+ /// Maximum number of files in the MPQ
+ ///
+ public uint MaxFileCount;
+
+ ///
+ /// Size of the hash table (in bytes)
+ ///
+ public uint HashTableSize;
+
+ ///
+ /// Effective size of the hash entry (in bits)
+ ///
+ public uint HashEntrySize;
+
+ ///
+ /// Total size of file index (in bits)
+ ///
+ public uint TotalIndexSize;
+
+ ///
+ /// Extra bits in the file index
+ ///
+ public uint IndexSizeExtra;
+
+ ///
+ /// Effective size of the file index (in bits)
+ ///
+ public uint IndexSize;
+
+ ///
+ /// Size of the block index subtable (in bytes)
+ ///
+ public uint BlockTableSize;
+
+ ///
+ /// HET hash table. Each entry is 8 bits.
+ ///
+ /// Size is derived from HashTableSize
+ public byte[] HashTable;
+
+ ///
+ /// Array of file indexes. Bit size of each entry is taken from dwTotalIndexSize.
+ /// Table size is taken from dwHashTableSize.
+ ///
+ public byte[][] FileIndexes;
+ }
+}
diff --git a/BurnOutSharp.Models/MoPaQ/PatchHeader.cs b/BurnOutSharp.Models/MoPaQ/PatchHeader.cs
new file mode 100644
index 00000000..09b5fff8
--- /dev/null
+++ b/BurnOutSharp.Models/MoPaQ/PatchHeader.cs
@@ -0,0 +1,118 @@
+using System.Runtime.InteropServices;
+
+namespace BurnOutSharp.Models.MoPaQ
+{
+ ///
+ /// Each incremental patch file in a patch MPQ starts with a header. It is supposed
+ /// to be a structure with variable length.
+ ///
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public class PatchHeader
+ {
+ #region PATCH Header
+
+ ///
+ /// 'PTCH'
+ ///
+ public uint PatchSignature { get; private set; }
+
+ ///
+ /// Size of the entire patch (decompressed)
+ ///
+ public int SizeOfPatchData { get; private set; }
+
+ ///
+ /// Size of the file before patch
+ ///
+ public int SizeBeforePatch { get; private set; }
+
+ ///
+ /// Size of file after patch
+ ///
+ public int SizeAfterPatch { get; private set; }
+
+ #endregion
+
+ #region MD5 Block
+
+ ///
+ /// 'MD5_'
+ ///
+ public uint Md5Signature { get; private set; }
+
+ ///
+ /// Size of the MD5 block, including the signature and size itself
+ ///
+ public int Md5BlockSize { get; private set; }
+
+ ///
+ /// MD5 of the original (unpached) file
+ ///
+ public byte[] Md5BeforePatch { get; private set; } = new byte[0x10];
+
+ ///
+ /// MD5 of the patched file
+ ///
+ public byte[] Md5AfterPatch { get; private set; } = new byte[0x10];
+
+ #endregion
+
+ #region XFRM Block
+
+ ///
+ /// 'XFRM'
+ ///
+ public uint XfrmSignature { get; private set; }
+
+ ///
+ /// Size of the XFRM block, includes XFRM header and patch data
+ ///
+ public int XfrmBlockSize { get; private set; }
+
+ ///
+ /// Type of patch ('BSD0' or 'COPY')
+ ///
+ public MoPaQPatchType PatchType { get; private set; }
+
+ #endregion
+
+ #region Patch Data - BSD0
+
+ ///
+ /// 'BSDIFF40' signature
+ ///
+ public ulong BsdiffSignature { get; private set; }
+
+ ///
+ /// Size of CTRL block (in bytes)
+ ///
+ public long CtrlBlockSize { get; private set; }
+
+ ///
+ /// Size of DATA block (in bytes)
+ ///
+ public long DataBlockSize { get; private set; }
+
+ ///
+ /// Size of file after applying the patch (in bytes)
+ ///
+ public long NewFileSize { get; private set; }
+
+ // TODO: Fill rest of data from http://zezula.net/en/mpq/patchfiles.html
+ // CTRL block
+ // DATA block
+ // EXTRA block
+
+ #endregion
+
+ #region Patch Data - COPY
+
+ ///
+ /// File data are simply replaced by the data in the patch.
+ ///
+ public byte[] PatchDataCopy { get; private set; }
+
+ #endregion
+ }
+}
diff --git a/BurnOutSharp.Models/MoPaQ/PatchInfo.cs b/BurnOutSharp.Models/MoPaQ/PatchInfo.cs
new file mode 100644
index 00000000..230eccfd
--- /dev/null
+++ b/BurnOutSharp.Models/MoPaQ/PatchInfo.cs
@@ -0,0 +1,38 @@
+using System.Runtime.InteropServices;
+
+namespace BurnOutSharp.Models.MoPaQ
+{
+ ///
+ /// This structure contains size of the patch, flags and also MD5 of the patch.
+ ///
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public class PatchInfo
+ {
+ ///
+ /// Length of patch info header, in bytes
+ ///
+ public uint Length;
+
+ ///
+ /// Flags. 0x80000000 = MD5 (?)
+ ///
+ public uint Flags;
+
+ ///
+ /// Uncompressed size of the patch file
+ ///
+ public uint DataSize;
+
+ ///
+ /// MD5 of the entire patch file after decompression
+ ///
+ /// 0x10 bytes
+ public byte[] MD5;
+
+ ///
+ /// The sector offset table (variable length)
+ ///
+ public uint[] SectorOffsetTable;
+ }
+}
diff --git a/BurnOutSharp.Models/MoPaQ/UserData.cs b/BurnOutSharp.Models/MoPaQ/UserData.cs
new file mode 100644
index 00000000..e0dcc644
--- /dev/null
+++ b/BurnOutSharp.Models/MoPaQ/UserData.cs
@@ -0,0 +1,37 @@
+using System.Runtime.InteropServices;
+
+namespace BurnOutSharp.Models.MoPaQ
+{
+ ///
+ /// MPQ User Data are optional, and is commonly used in custom maps for
+ /// Starcraft II. If MPQ User Data header is present, it contains an offset,
+ /// from where the MPQ header should be searched.
+ ///
+ ///
+ [StructLayout(LayoutKind.Sequential)]
+ public class UserData
+ {
+ ///
+ /// The user data signature
+ ///
+ ///
+ public uint Signature;
+
+ ///
+ /// Maximum size of the user data
+ ///
+ public uint UserDataSize;
+
+ ///
+ /// Offset of the MPQ header, relative to the beginning of this header
+ ///
+ public uint HeaderOffset;
+
+ ///
+ /// Appears to be size of user data header (Starcraft II maps)
+ ///
+ public uint UserDataHeaderSize;
+
+ // TODO: Does this area contain extra data that should be read in?
+ }
+}
diff --git a/BurnOutSharp/FileType/MPQ.cs b/BurnOutSharp/FileType/MPQ.cs
index dfa50ff0..43141f2d 100644
--- a/BurnOutSharp/FileType/MPQ.cs
+++ b/BurnOutSharp/FileType/MPQ.cs
@@ -99,1327 +99,535 @@ namespace BurnOutSharp.FileType
// http://zezula.net/en/mpq/mpqformat.html
#region TEMPORARY AREA FOR MPQ FORMAT
- ///
- /// MPQ (MoPaQ) is an archive format developed by Blizzard Entertainment,
- /// purposed for storing data files, images, sounds, music and videos for
- /// their games. The name MoPaQ comes from the author of the format,
- /// Mike O'Brien (Mike O'brien PaCK).
- ///
internal class MoPaQArchive
{
- #region Properties
+ //#region Serialization
- // Data before archive, ignored
+ /////
+ ///// Deserialize at into a new MoPaQArchive object
+ /////
+ //public static MoPaQArchive Deserialize(byte[] data, ref int dataPtr)
+ //{
+ // if (data == null || dataPtr < 0)
+ // return null;
- ///
- /// MPQ User Data (optional)
- ///
- public MoPaQUserData UserData { get; private set; }
+ // MoPaQArchive archive = new MoPaQArchive();
- ///
- /// MPQ Header (required)
- ///
- public MoPaQArchiveHeader ArchiveHeader { get; private set; }
+ // // Check for User Data
+ // uint possibleSignature = BitConverter.ToUInt32(data, dataPtr);
+ // if (possibleSignature == MoPaQUserData.SignatureValue)
+ // {
+ // // Save the current position for offset correction
+ // int basePtr = dataPtr;
- // Files (optional)
- // Special files (optional)
+ // // Deserialize the user data, returning null if invalid
+ // archive.UserData = MoPaQUserData.Deserialize(data, ref dataPtr);
+ // if (archive.UserData == null)
+ // return null;
- ///
- /// HET Table (optional)
- ///
- public MoPaQHetTable HetTable { get; private set; }
+ // // Set the starting position according to the header offset
+ // dataPtr = basePtr + (int)archive.UserData.HeaderOffset;
+ // }
- ///
- /// BET Table (optional)
- ///
- public MoPaQBetTable BetTable { get; private set; }
+ // // 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;
+ // }
- ///
- /// Hash Table (optional)
- ///
- public MoPaQHashEntry[] HashTable { get; private set; }
+ // // If we don't have a header, return null
+ // if (archive.ArchiveHeader == null)
+ // return null;
- ///
- /// Block Table (optional)
- ///
- public MoPaQBlockEntry[] BlockTable { get; private set; }
+ // // 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
- ///
- /// Hi-Block Table (optional)
- ///
- ///
- /// Since World of Warcraft - The Burning Crusade, Blizzard extended
- /// the MPQ format to support archives larger than 4GB. The hi-block
- /// table holds the higher 16-bits of the file position in the MPQ.
- /// Hi-block table is plain array of 16-bit values. This table is
- /// not encrypted.
- ///
- public short[] HiBlockTable { get; private set; }
+ // return archive;
+ //}
- // 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
+ //#endregion
}
- ///
- /// MPQ User Data are optional, and is commonly used in custom maps for
- /// Starcraft II. If MPQ User Data header is present, it contains an offset,
- /// from where the MPQ header should be searched.
- ///
internal class MoPaQUserData
{
- #region Constants
+ //#region Constants
- public const int Size = 0x10;
+ //public const int Size = 0x10;
- ///
- /// Human-readable signature
- ///
- public static readonly string SignatureString = $"MPQ{(char)0x1B}";
+ /////
+ ///// Human-readable signature
+ /////
+ //public static readonly string SignatureString = $"MPQ{(char)0x1B}";
- ///
- /// Signature as an unsigned Int32 value
- ///
- public const uint SignatureValue = 0x1B51504D;
+ /////
+ ///// Signature as an unsigned Int32 value
+ /////
+ //public const uint SignatureValue = 0x1B51504D;
- ///
- /// Signature as a byte array
- ///
- public static readonly byte[] SignatureBytes = new byte[] { 0x4D, 0x50, 0x51, 0x1B };
+ /////
+ ///// Signature as a byte array
+ /////
+ //public static readonly byte[] SignatureBytes = new byte[] { 0x4D, 0x50, 0x51, 0x1B };
- #endregion
+ //#endregion
- #region Properties
+ //#region Serialization
- ///
- /// The user data signature
- ///
- ///
- public uint Signature { get; private set; }
+ /////
+ ///// Deserialize at into a new MoPaQUserData object
+ /////
+ //public static MoPaQUserData Deserialize(byte[] data, ref int dataPtr)
+ //{
+ // if (data == null || dataPtr < 0)
+ // return null;
- ///
- /// Maximum size of the user data
- ///
- public uint UserDataSize { get; private set; }
+ // MoPaQUserData userData = new MoPaQUserData();
- ///
- /// Offset of the MPQ header, relative to the beginning of this header
- ///
- public uint HeaderOffset { get; private set; }
+ // userData.Signature = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
+ // if (userData.Signature != SignatureValue)
+ // return null;
- ///
- /// Appears to be size of user data header (Starcraft II maps)
- ///
- public uint UserDataHeaderSize { get; private set; }
+ // userData.UserDataSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
+ // userData.HeaderOffset = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
+ // userData.UserDataHeaderSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
- // TODO: Does this area contain extra data that should be read in?
+ // return userData;
+ //}
- #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
+ //#endregion
}
- ///
- /// MoPaQ archive header
- ///
internal class MoPaQArchiveHeader
{
- #region Constants
+ //#region Constants
- #region Signatures
+ //#region Signatures
- ///
- /// Human-readable signature
- ///
- public static readonly string SignatureString = $"MPQ{(char)0x1A}";
+ /////
+ ///// Human-readable signature
+ /////
+ //public static readonly string SignatureString = $"MPQ{(char)0x1A}";
- ///
- /// Signature as an unsigned Int32 value
- ///
- public const uint SignatureValue = 0x1A51504D;
+ /////
+ ///// 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 };
+ /////
+ ///// Signature as a byte array
+ /////
+ //public static readonly byte[] SignatureBytes = new byte[] { 0x4D, 0x50, 0x51, 0x1A };
- #endregion
+ //#endregion
- #region Header Sizes
+ //#region Header Sizes
- public const int HeaderVersion1Size = 0x20;
+ //public const int HeaderVersion1Size = 0x20;
- public const int HeaderVersion2Size = 0x2C;
+ //public const int HeaderVersion2Size = 0x2C;
- public const int HeaderVersion3Size = 0x44;
+ //public const int HeaderVersion3Size = 0x44;
- public const int HeaderVersion4Size = 0xD0;
+ //public const int HeaderVersion4Size = 0xD0;
- #endregion
+ //#endregion
- #endregion
+ //#endregion
- #region V1 Properties
+ //#region Serialization
- ///
- /// The MPQ archive signature
- ///
- public uint Signature { get; private set; }
+ /////
+ ///// Deserialize at into a new MoPaQArchiveHeader object
+ /////
+ //public static MoPaQArchiveHeader Deserialize(byte[] data, ref int dataPtr)
+ //{
+ // if (data == null || dataPtr < 0)
+ // return null;
- ///
- /// Size of the archive header
- ///
- public uint HeaderSize { get; private set; }
+ // MoPaQArchiveHeader archiveHeader = new MoPaQArchiveHeader();
- ///
- /// Size of MPQ archive
- ///
- ///
- /// This field is deprecated in the Burning Crusade MoPaQ format, and the size of the archive
- /// 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 uint ArchiveSize { get; private set; }
+ // // V1 - Common
+ // archiveHeader.Signature = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
+ // if (archiveHeader.Signature != SignatureValue)
+ // return null;
- ///
- /// 0 = Format 1 (up to The Burning Crusade)
- /// 1 = Format 2 (The Burning Crusade and newer)
- /// 2 = Format 3 (WoW - Cataclysm beta or newer)
- /// 3 = Format 4 (WoW - Cataclysm beta or newer)
- ///
- public ushort FormatVersion { get; private set; }
+ // 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;
- ///
- /// 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 ushort BlockSize { get; private set; }
+ // // 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;
+ // }
- ///
- /// Offset to the beginning of the hash table, relative to the beginning of the archive.
- ///
- public uint HashTablePosition { get; private set; }
+ // // 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;
+ // }
- ///
- /// Offset to the beginning of the block table, relative to the beginning of the archive.
- ///
- public uint BlockTablePosition { get; private set; }
+ // // 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;
- ///
- /// 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 uint HashTableSize { get; private set; }
+ // 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;
+ // }
- ///
- /// Number of entries in the block table
- ///
- public uint BlockTableSize { get; private set; }
+ // return archiveHeader;
+ //}
- #endregion
-
- #region V2 Properties
-
- ///
- /// Offset to the beginning of array of 16-bit high parts of file offsets.
- ///
- public ulong HiBlockTablePosition { get; private set; }
-
- ///
- /// High 16 bits of the hash table offset for large archives.
- ///
- public ushort HashTablePositionHi { get; private set; }
-
- ///
- /// High 16 bits of the block table offset for large archives.
- ///
- public ushort BlockTablePositionHi { get; private set; }
-
- #endregion
-
- #region V3 Properties
-
- ///
- /// 64-bit version of the archive size
- ///
- public ulong ArchiveSizeLong { get; private set; }
-
- ///
- /// 64-bit position of the BET table
- ///
- public ulong BetTablePosition { get; private set; }
-
- ///
- /// 64-bit position of the HET table
- ///
- public ulong HetTablePosition { get; private set; }
-
- #endregion
-
- #region V4 Properties
-
- ///
- /// Compressed size of the hash table
- ///
- public ulong HashTableSizeLong { get; private set; }
-
- ///
- /// Compressed size of the block table
- ///
- public ulong BlockTableSizeLong { get; private set; }
-
- ///
- /// Compressed size of the hi-block table
- ///
- public ulong HiBlockTableSize { get; private set; }
-
- ///
- /// Compressed size of the HET block
- ///
- public ulong HetTableSize { get; private set; }
-
- ///
- /// Compressed size of the BET block
- ///
- 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 uint RawChunkSize { get; private set; }
-
- ///
- /// MD5 of the block table before decryption
- ///
- public byte[] BlockTableMD5 { get; private set; } = new byte[0x10];
-
- ///
- /// MD5 of the hash table before decryption
- ///
- public byte[] HashTableMD5 { get; private set; } = new byte[0x10];
-
- ///
- /// MD5 of the hi-block table
- ///
- public byte[] HiBlockTableMD5 { get; private set; } = new byte[0x10];
-
- ///
- /// MD5 of the BET table before decryption
- ///
- public byte[] BetTableMD5 { get; private set; } = new byte[0x10];
-
- ///
- /// MD5 of the HET table before decryption
- ///
- public byte[] HetTableMD5 { get; private set; } = new byte[0x10];
-
- ///
- /// MD5 of the MPQ header from signature to (including) HetTableMD5
- ///
- 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
+ //#endregion
}
- ///
- /// The HET table is present if the HetTablePos64 member of MPQ header is
- /// set to nonzero. This table can fully replace hash table. Depending on
- /// MPQ size, the pair of HET&BET table can be more efficient than Hash&Block
- /// table. HET table can be encrypted and compressed.
- ///
internal class MoPaQHetTable
{
- #region Constants
+ //#region Constants
- public const int Size = 0x44;
+ //public const int Size = 0x44;
- ///
- /// Human-readable signature
- ///
- public static readonly string SignatureString = $"HET{(char)0x1A}";
+ /////
+ ///// Human-readable signature
+ /////
+ //public static readonly string SignatureString = $"HET{(char)0x1A}";
- ///
- /// Signature as an unsigned Int32 value
- ///
- public const uint SignatureValue = 0x1A544548;
+ /////
+ ///// Signature as an unsigned Int32 value
+ /////
+ //public const uint SignatureValue = 0x1A544548;
- ///
- /// Signature as a byte array
- ///
- public static readonly byte[] SignatureBytes = new byte[] { 0x48, 0x45, 0x54, 0x1A };
+ /////
+ ///// Signature as a byte array
+ /////
+ //public static readonly byte[] SignatureBytes = new byte[] { 0x48, 0x45, 0x54, 0x1A };
- #endregion
+ //#endregion
- // TODO: Extract this out and make in common between HET and BET
- #region Common Table Headers
+ //#region Serialization
- ///
- /// 'HET\x1A'
- ///
- public uint Signature { get; private set; }
+ /////
+ ///// Deserialize at into a new MoPaQHetTable object
+ /////
+ //public static MoPaQHetTable Deserialize(byte[] data, ref int dataPtr)
+ //{
+ // if (data == null || dataPtr < 0)
+ // return null;
- ///
- /// Version. Seems to be always 1
- ///
- public uint Version { get; private set; }
+ // MoPaQHetTable hetTable = new MoPaQHetTable();
- ///
- /// Size of the contained table
- ///
- public uint DataSize { get; private set; }
+ // // Common Headers
+ // hetTable.Signature = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
+ // if (hetTable.Signature != SignatureValue)
+ // return null;
- #endregion
+ // hetTable.Version = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
+ // hetTable.DataSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
- #region Properties
+ // // 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;
- ///
- /// Size of the entire hash table, including the header (in bytes)
- ///
- public uint TableSize { get; private set; }
+ // hetTable.HashTable = new byte[hetTable.HashTableSize];
+ // Array.Copy(data, dataPtr, hetTable.HashTable, 0, hetTable.HashTableSize);
+ // dataPtr += (int)hetTable.HashTableSize;
- ///
- /// Maximum number of files in the MPQ
- ///
- public uint MaxFileCount { get; private set; }
+ // // TODO: Populate the file indexes array
+ // hetTable.FileIndexes = new byte[(int)hetTable.HashTableSize][];
- ///
- /// Size of the hash table (in bytes)
- ///
- public uint HashTableSize { get; private set; }
+ // return hetTable;
+ //}
- ///
- /// Effective size of the hash entry (in bits)
- ///
- public uint HashEntrySize { get; private set; }
-
- ///
- /// Total size of file index (in bits)
- ///
- public uint TotalIndexSize { get; private set; }
-
- ///
- /// Extra bits in the file index
- ///
- public uint IndexSizeExtra { get; private set; }
-
- ///
- /// Effective size of the file index (in bits)
- ///
- public uint IndexSize { get; private set; }
-
- ///
- /// Size of the block index subtable (in bytes)
- ///
- public uint BlockTableSize { get; private set; }
-
- ///
- /// HET hash table. Each entry is 8 bits.
- ///
- /// Size is derived from HashTableSize
- public byte[] HashTable { get; private set; }
-
- ///
- /// 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
+ //#endregion
}
- ///
- /// he BET table is present if the BetTablePos64 member of MPQ header is set
- /// to nonzero. BET table is a successor of classic block table, and can fully
- /// replace it. It is also supposed to be more effective.
- ///
internal class MoPaQBetTable
{
- #region Constants
+ //#region Constants
- public const int Size = 0x88;
+ //public const int Size = 0x88;
- ///
- /// Human-readable signature
- ///
- public static readonly string SignatureString = $"BET{(char)0x1A}";
+ /////
+ ///// Human-readable signature
+ /////
+ //public static readonly string SignatureString = $"BET{(char)0x1A}";
- ///
- /// Signature as an unsigned Int32 value
- ///
- public const uint SignatureValue = 0x1A544542;
+ /////
+ ///// Signature as an unsigned Int32 value
+ /////
+ //public const uint SignatureValue = 0x1A544542;
- ///
- /// Signature as a byte array
- ///
- public static readonly byte[] SignatureBytes = new byte[] { 0x42, 0x45, 0x54, 0x1A };
+ /////
+ ///// Signature as a byte array
+ /////
+ //public static readonly byte[] SignatureBytes = new byte[] { 0x42, 0x45, 0x54, 0x1A };
- #endregion
+ //#endregion
- // TODO: Extract this out and make in common between HET and BET
- #region Common Table Headers
+ //#region Serialization
- ///
- /// 'BET\x1A'
- ///
- public uint Signature { get; private set; }
+ /////
+ ///// Deserialize at into a new MoPaQBetTable object
+ /////
+ //public static MoPaQBetTable Deserialize(byte[] data, ref int dataPtr)
+ //{
+ // if (data == null || dataPtr < 0)
+ // return null;
- ///
- /// Version. Seems to be always 1
- ///
- public uint Version { get; private set; }
+ // MoPaQBetTable betTable = new MoPaQBetTable();
- ///
- /// Size of the contained table
- ///
- public uint DataSize { get; private set; }
+ // // Common Headers
+ // betTable.Signature = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
+ // if (betTable.Signature != SignatureValue)
+ // return null;
- #endregion
+ // betTable.Version = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
+ // betTable.DataSize = BitConverter.ToUInt32(data, dataPtr); dataPtr += 4;
- #region Properties
+ // // 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;
- ///
- /// Size of the entire hash table, including the header (in bytes)
- ///
- public uint TableSize { get; private set; }
+ // 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;
- ///
- /// Number of files in the BET table
- ///
- public uint FileCount { get; private set; }
+ // 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;
- ///
- /// Unknown, set to 0x10
- ///
- public uint Unknown { get; private set; }
+ // 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;
- ///
- /// Size of one table entry (in bits)
- ///
- public uint TableEntrySize { get; private set; }
+ // betTable.FlagsArray = new uint[betTable.FlagCount];
+ // Buffer.BlockCopy(data, dataPtr, betTable.FlagsArray, 0, (int)betTable.FlagCount * 4);
+ // dataPtr += (int)betTable.FlagCount * 4;
- ///
- /// Bit index of the file position (within the entry record)
- ///
- public uint FilePositionBitIndex { get; private set; }
+ // // TODO: Populate the file table
+ // // TODO: Populate the hash table
- ///
- /// Bit index of the file size (within the entry record)
- ///
- public uint FileSizeBitIndex { get; private set; }
+ // return betTable;
+ //}
- ///
- /// Bit index of the compressed size (within the entry record)
- ///
- public uint CompressedSizeBitIndex { get; private set; }
-
- ///
- /// Bit index of the flag index (within the entry record)
- ///
- public uint FlagIndexBitIndex { get; private set; }
-
- ///
- /// Bit index of the ??? (within the entry record)
- ///
- public uint UnknownBitIndex { get; private set; }
-
- ///
- /// Bit size of file position (in the entry record)
- ///
- public uint FilePositionBitCount { get; private set; }
-
- ///
- /// Bit size of file size (in the entry record)
- ///
- public uint FileSizeBitCount { get; private set; }
-
- ///
- /// Bit size of compressed file size (in the entry record)
- ///
- public uint CompressedSizeBitCount { get; private set; }
-
- ///
- /// Bit size of flags index (in the entry record)
- ///
- public uint FlagIndexBitCount { get; private set; }
-
- ///
- /// Bit size of ??? (in the entry record)
- ///
- public uint UnknownBitCount { get; private set; }
-
- ///
- /// Total size of the BET hash
- ///
- public uint TotalBetHashSize { get; private set; }
-
- ///
- /// Extra bits in the BET hash
- ///
- public uint BetHashSizeExtra { get; private set; }
-
- ///
- /// Effective size of BET hash (in bits)
- ///
- public uint BetHashSize { get; private set; }
-
- ///
- /// Size of BET hashes array, in bytes
- ///
- public uint BetHashArraySize { get; private set; }
-
- ///
- /// Number of flags in the following array
- ///
- 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 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.
-
- // 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
+ //#endregion
}
- ///
- /// Hash table is used for searching files by name. The file name is converted to
- /// two 32-bit hash values, which are then used for searching in the table. The size
- /// of the hash table must always be a power of two. Each entry in the hash table
- /// also contains file locale and offset into block table. Size of one entry of hash
- /// table is 16 bytes.
- ///
internal class MoPaQHashEntry
{
- #region Constants
+ //#region Constants
- public const int Size = 0x10;
+ //public const int Size = 0x10;
- #endregion
+ //#endregion
- #region Properties
+ //#region Serialization
- ///
- /// The hash of the full file name (part A)
- ///
- public uint NameHashPartA { get; private set; }
+ /////
+ ///// Deserialize at into a new MoPaQHashEntry object
+ /////
+ //public static MoPaQHashEntry Deserialize(byte[] data, ref int dataPtr)
+ //{
+ // if (data == null || dataPtr < 0)
+ // return null;
- ///
- /// The hash of the full file name (part B)
- ///
- public uint NameHashPartB { get; private set; }
+ // MoPaQHashEntry hashEntry = new MoPaQHashEntry();
- ///
- /// The language of the file. This is a Windows LANGID data type, and uses the same values.
- /// 0 indicates the default language (American English), or that the file is language-neutral.
- ///
- public MoPaQLocale Locale { get; private set; }
+ // 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;
- ///
- /// The platform the file is used for. 0 indicates the default platform.
- /// No other values have been observed.
- ///
- public ushort Platform { get; private set; }
+ // return hashEntry;
+ //}
- ///
- /// If the hash table entry is valid, this is the index into the block table of the file.
- /// Otherwise, one of the following two values:
- /// - FFFFFFFFh: Hash table entry is empty, and has always been empty.
- /// Terminates searches for a given file.
- /// - FFFFFFFEh: Hash table entry is empty, but was valid at some point (a deleted file).
- /// Does not terminate searches for a given file.
- ///
- 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
+ //#endregion
}
- internal enum MoPaQLocale : short
- {
- Neutral = 0,
- AmericanEnglish = 0,
- ChineseTaiwan = 0x404,
- Czech = 0x405,
- German = 0x407,
- English = 0x409,
- Spanish = 0x40A,
- French = 0x40C,
- Italian = 0x410,
- Japanese = 0x411,
- Korean = 0x412,
- Polish = 0x415,
- Portuguese = 0x416,
- Russian = 0x419,
- EnglishUK = 0x809,
- }
-
- ///
- /// Block table contains informations about file sizes and way of their storage within
- /// the archive. It also contains the position of file content in the archive. Size
- /// of block table entry is (like hash table entry). The block table is also encrypted.
- ///
internal class MoPaQBlockEntry
{
- #region Constants
+ //#region Constants
- public const int Size = 0x10;
+ //public const int Size = 0x10;
- #endregion
+ //#endregion
- #region Properties
+ //#region Serialization
- ///
- /// Offset of the beginning of the file data, relative to the beginning of the archive.
- ///
- public uint FilePosition { get; private set; }
+ /////
+ ///// Deserialize at into a new MoPaQBlockEntry object
+ /////
+ //public static MoPaQBlockEntry Deserialize(byte[] data, ref int dataPtr)
+ //{
+ // if (data == null || dataPtr < 0)
+ // return null;
- ///
- /// Compressed file size
- ///
- public uint CompressedSize { get; private set; }
+ // MoPaQBlockEntry blockEntry = new MoPaQBlockEntry();
- ///
- /// Size of uncompressed file
- ///
- public uint UncompressedSize { get; private set; }
+ // 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;
- ///
- /// Flags for the file.
- ///
- public MoPaQFileFlags Flags { get; private set; }
+ // return blockEntry;
+ //}
- #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
+ //#endregion
}
- [Flags]
- internal enum MoPaQFileFlags : uint
- {
- ///
- /// File is compressed using PKWARE Data compression library
- ///
- MPQ_FILE_IMPLODE = 0x00000100,
-
- ///
- /// File is compressed using combination of compression methods
- ///
- MPQ_FILE_COMPRESS = 0x00000200,
-
- ///
- /// The file is encrypted
- ///
- MPQ_FILE_ENCRYPTED = 0x00010000,
-
- ///
- /// The decryption key for the file is altered according to the
- /// position of the file in the archive
- ///
- MPQ_FILE_FIX_KEY = 0x00020000,
-
- ///
- /// The file contains incremental patch for an existing file in base MPQ
- ///
- MPQ_FILE_PATCH_FILE = 0x00100000,
-
- ///
- /// Instead of being divided to 0x1000-bytes blocks, the file is stored
- /// as single unit
- ///
- MPQ_FILE_SINGLE_UNIT = 0x01000000,
-
- ///
- /// File is a deletion marker, indicating that the file no longer exists.
- /// This is used to allow patch archives to delete files present in
- /// lower-priority archives in the search chain. The file usually has
- /// length of 0 or 1 byte and its name is a hash
- ///
- MPQ_FILE_DELETE_MARKER = 0x02000000,
-
- ///
- /// File has checksums for each sector (explained in the File Data section).
- /// Ignored if file is not compressed or imploded.
- ///
- MPQ_FILE_SECTOR_CRC = 0x04000000,
-
- ///
- /// Set if file exists, reset when the file was deleted
- ///
- MPQ_FILE_EXISTS = 0x80000000,
- }
-
- ///
- /// This structure contains size of the patch, flags and also MD5 of the patch.
- ///
internal class MoPaQPatchInfo
{
- #region Properties
+ //#region Serialization
- ///
- /// Length of patch info header, in bytes
- ///
- public uint Length { get; private set; }
+ /////
+ ///// Deserialize at into a new MoPaQPatchInfo object
+ /////
+ //public static MoPaQPatchInfo Deserialize(byte[] data, ref int dataPtr)
+ //{
+ // if (data == null || dataPtr < 0)
+ // return null;
- ///
- /// Flags. 0x80000000 = MD5 (?)
- ///
- public uint Flags { get; private set; }
+ // MoPaQPatchInfo patchInfo = new MoPaQPatchInfo();
- ///
- /// Uncompressed size of the patch file
- ///
- public uint DataSize { get; private set; }
+ // 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;
- ///
- /// MD5 of the entire patch file after decompression
- ///
- public byte[] MD5 { get; private set; } = new byte[0x10];
+ // // TODO: Fill the sector offset table
- ///
- /// The sector offset table (variable length)
- ///
- public uint[] SectorOffsetTable { get; private set; }
+ // return patchInfo;
+ //}
- #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
+ //#endregion
}
- ///
- /// Each incremental patch file in a patch MPQ starts with a header. It is supposed
- /// to be a structure with variable length.
- ///
internal class MoPaQPatchHeader
{
- #region Constants
+ //#region Constants
- #region Signatures
+ //#region Signatures
- #region Patch Header
+ //#region Patch Header
- ///
- /// Human-readable signature
- ///
- public static readonly string PatchSignatureString = $"PTCH";
+ /////
+ ///// Human-readable signature
+ /////
+ //public static readonly string PatchSignatureString = $"PTCH";
- ///
- /// Signature as an unsigned Int32 value
- ///
- public const uint PatchSignatureValue = 0x48435450;
+ /////
+ ///// Signature as an unsigned Int32 value
+ /////
+ //public const uint PatchSignatureValue = 0x48435450;
- ///
- /// Signature as a byte array
- ///
- public static readonly byte[] PatchSignatureBytes = new byte[] { 0x50, 0x54, 0x43, 0x48 };
+ /////
+ ///// Signature as a byte array
+ /////
+ //public static readonly byte[] PatchSignatureBytes = new byte[] { 0x50, 0x54, 0x43, 0x48 };
- #endregion
+ //#endregion
- #region MD5 Block
+ //#region MD5 Block
- ///
- /// Human-readable signature
- ///
- public static readonly string Md5SignatureString = $"MD5_";
+ /////
+ ///// Human-readable signature
+ /////
+ //public static readonly string Md5SignatureString = $"MD5_";
- ///
- /// Signature as an unsigned Int32 value
- ///
- public const uint Md5SignatureValue = 0x5F35444D;
+ /////
+ ///// Signature as an unsigned Int32 value
+ /////
+ //public const uint Md5SignatureValue = 0x5F35444D;
- ///
- /// Signature as a byte array
- ///
- public static readonly byte[] Md5SignatureBytes = new byte[] { 0x4D, 0x44, 0x35, 0x5F };
+ /////
+ ///// Signature as a byte array
+ /////
+ //public static readonly byte[] Md5SignatureBytes = new byte[] { 0x4D, 0x44, 0x35, 0x5F };
- #endregion
+ //#endregion
- #region XFRM Block
+ //#region XFRM Block
- ///
- /// Human-readable signature
- ///
- public static readonly string XFRMSignatureString = $"XFRM";
+ /////
+ ///// Human-readable signature
+ /////
+ //public static readonly string XFRMSignatureString = $"XFRM";
- ///
- /// Signature as an unsigned Int32 value
- ///
- public const uint XFRMSignatureValue = 0x4D524658;
+ /////
+ ///// Signature as an unsigned Int32 value
+ /////
+ //public const uint XFRMSignatureValue = 0x4D524658;
- ///
- /// Signature as a byte array
- ///
- public static readonly byte[] XFRMSignatureBytes = new byte[] { 0x58, 0x46, 0x52, 0x4D };
+ /////
+ ///// Signature as a byte array
+ /////
+ //public static readonly byte[] XFRMSignatureBytes = new byte[] { 0x58, 0x46, 0x52, 0x4D };
- #endregion
+ //#endregion
- #region BSDIFF Patch Type
+ //#region BSDIFF Patch Type
- ///
- /// Human-readable signature
- ///
- public static readonly string BSDIFF40SignatureString = $"BSDIFF40";
+ /////
+ ///// Human-readable signature
+ /////
+ //public static readonly string BSDIFF40SignatureString = $"BSDIFF40";
- ///
- /// Signature as an unsigned Int64 value
- ///
- public const ulong BSDIFF40SignatureValue = 0x3034464649445342;
+ /////
+ ///// Signature as an unsigned Int64 value
+ /////
+ //public const ulong BSDIFF40SignatureValue = 0x3034464649445342;
- ///
- /// Signature as a byte array
- ///
- public static readonly byte[] BSDIFF40SignatureBytes = new byte[] { 0x42, 0x53, 0x44, 0x49, 0x46, 0x46, 0x34, 0x30 };
+ /////
+ ///// Signature as a byte array
+ /////
+ //public static readonly byte[] BSDIFF40SignatureBytes = new byte[] { 0x42, 0x53, 0x44, 0x49, 0x46, 0x46, 0x34, 0x30 };
- #endregion
+ //#endregion
- #endregion
+ //#endregion
- #endregion
-
- #region Properties
-
- #region PATCH Header
-
- ///
- /// 'PTCH'
- ///
- public uint PatchSignature { get; private set; }
-
- ///
- /// Size of the entire patch (decompressed)
- ///
- public int SizeOfPatchData { get; private set; }
-
- ///
- /// Size of the file before patch
- ///
- public int SizeBeforePatch { get; private set; }
-
- ///
- /// Size of file after patch
- ///
- public int SizeAfterPatch { get; private set; }
-
- #endregion
-
- #region MD5 Block
-
- ///
- /// 'MD5_'
- ///
- public uint Md5Signature { get; private set; }
-
- ///
- /// Size of the MD5 block, including the signature and size itself
- ///
- public int Md5BlockSize { get; private set; }
-
- ///
- /// MD5 of the original (unpached) file
- ///
- public byte[] Md5BeforePatch { get; private set; } = new byte[0x10];
-
- ///
- /// MD5 of the patched file
- ///
- public byte[] Md5AfterPatch { get; private set; } = new byte[0x10];
-
- #endregion
-
- #region XFRM Block
-
- ///
- /// 'XFRM'
- ///
- public uint XfrmSignature { get; private set; }
-
- ///
- /// Size of the XFRM block, includes XFRM header and patch data
- ///
- public int XfrmBlockSize { get; private set; }
-
- ///
- /// Type of patch ('BSD0' or 'COPY')
- ///
- public MoPaQPatchType PatchType { get; private set; }
-
- #endregion
-
- #region Patch Data - BSD0
-
- ///
- /// 'BSDIFF40' signature
- ///
- public ulong BsdiffSignature { get; private set; }
-
- ///
- /// Size of CTRL block (in bytes)
- ///
- public long CtrlBlockSize { get; private set; }
-
- ///
- /// Size of DATA block (in bytes)
- ///
- public long DataBlockSize { get; private set; }
-
- ///
- /// Size of file after applying the patch (in bytes)
- ///
- public long NewFileSize { get; private set; }
-
- // TODO: Fill rest of data from http://zezula.net/en/mpq/patchfiles.html
- // CTRL block
- // DATA block
- // EXTRA block
-
- #endregion
-
- #region Patch Data - COPY
-
- ///
- /// File data are simply replaced by the data in the patch.
- ///
- public byte[] PatchDataCopy { get; private set; }
-
- #endregion
-
- #endregion
- }
-
- internal enum MoPaQPatchType : uint
- {
- ///
- /// Blizzard-modified version of BSDIFF40 incremental patch
- ///
- BSD0 = 0x30445342,
-
- ///
- /// Unknown
- ///
- BSDP = 0x50445342,
-
- ///
- /// Plain replace
- ///
- COPY = 0x59504F43,
-
- ///
- /// Unknown
- ///
- COUP = 0x50554F43,
-
- ///
- /// Unknown
- ///
- CPOG = 0x474F5043,
+ //#endregion
}
#endregion