From 0915c7eccdf10387d4a6f63e494739bd8858c51a Mon Sep 17 00:00:00 2001 From: Matt Nadareski Date: Wed, 6 Jul 2022 13:39:54 -0700 Subject: [PATCH] Add a few more things for MPQ --- BurnOutSharp/FileType/MPQ.cs | 266 ++++++++++++++++++++++++++++++++++- 1 file changed, 265 insertions(+), 1 deletion(-) diff --git a/BurnOutSharp/FileType/MPQ.cs b/BurnOutSharp/FileType/MPQ.cs index af4c68a3..3a708ea0 100644 --- a/BurnOutSharp/FileType/MPQ.cs +++ b/BurnOutSharp/FileType/MPQ.cs @@ -763,7 +763,7 @@ namespace BurnOutSharp.FileType } /// - /// lock table contains informations about file sizes and way of their storage within + /// 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. /// @@ -855,6 +855,270 @@ namespace BurnOutSharp.FileType MPQ_FILE_EXISTS = 0x80000000, } + /// + /// This structure contains size of the patch, flags and also MD5 of the patch. + /// + internal class MoPaQPatchInfo + { + #region Properties + + /// + /// Length of patch info header, in bytes + /// + public int Length { get; private set; } + + /// + /// Flags. 0x80000000 = MD5 (?) + /// + public uint Flags { get; private set; } + + /// + /// Uncompressed size of the patch file + /// + public int DataSize { get; private set; } + + /// + /// MD5 of the entire patch file after decompression + /// + public byte[] MD5 { get; private set; } = new byte[0x10]; + + /// + /// The sector offset table (variable length) + /// + public int[] SectorOffsetTable { get; private set; } + + #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 Signatures + + #region Patch Header + + /// + /// Human-readable signature + /// + public static readonly string PatchSignatureString = $"PTCH"; + + /// + /// 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 }; + + #endregion + + #region MD5 Block + + /// + /// Human-readable signature + /// + public static readonly string Md5SignatureString = $"MD5_"; + + /// + /// 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 }; + + #endregion + + #region XFRM Block + + /// + /// Human-readable signature + /// + public static readonly string XFRMSignatureString = $"XFRM"; + + /// + /// 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 }; + + #endregion + + #region BSDIFF Patch Type + + /// + /// Human-readable signature + /// + public static readonly string BSDIFF40SignatureString = $"BSDIFF40"; + + /// + /// 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 }; + + + #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 } }