MPQ Archives
Inkrementální soubory (patche)
Inkrementální soubory byly poprvé pozorovány v BETA verzi hry World of Warcraft - Cataclysm. Jsou to soubory v archivech MPQ, které maji nastaven atribut MPQ_FILE_PATCH_FILE. Inkrementální soubory mohou být uloženy po jednotlivých sektorech nebo v souvislém bloku (single unit). Některé proměnné v metadatech souboru mají pozměněný význam oproti běžným souborům:
Tato struktura obsahuje velikost inkrementálního souboru, atributy a MD5. V jazyce C je struktura definovaná takto:
// Patch file header struct TPatchInfo { DWORD dwLength; // Length of patch info header, in bytes DWORD dwFlags; // Flags. 0x80000000 = MD5 (?) DWORD dwDataSize; // Uncompressed size of the patch file BYTE md5[0x10]; // MD5 of the entire patch file after decompression // Followed by the sector offset table (variable length) };
TPatchInfo je součástí metadat - obsahuje informace potřebné k přečtení souboru z MPQ.
Každý inkrementální soubor začíná strukturou TPatchHeader. Tato struktura má pravděpodobně proměnnou délku, a v jazyce C je zapsána takto:
// Header for PTCH files struct TPatchHeader { //-- PATCH header ----------------------------------- DWORD dwSignature; // 'PTCH' DWORD dwSizeOfPatchData; // Size of the entire patch (decompressed) DWORD dwSizeBeforePatch; // Size of the file before patch DWORD dwSizeAfterPatch; // Size of file after patch //-- MD5 block -------------------------------------- DWORD dwMD5; // 'MD5_' DWORD dwMd5BlockSize; // Size of the MD5 block, including the signature and size itself BYTE md5_before_patch[0x10]; // MD5 of the original (unpached) file BYTE md5_after_patch[0x10]; // MD5 of the patched file //-- XFRM block ------------------------------------- DWORD dwXFRM; // 'XFRM' DWORD dwXfrmBlockSize; // Size of the XFRM block, includes XFRM header and patch data DWORD dwPatchType; // Type of patch ('BSD0' or 'COPY') // Followed by the patch data };
Data pro aktualizaci následují bezprostředně za hlavičkou. Typ dat závisí na hodnotě proměnné dwPatchType v hlavičce. Velikost dat je uložena taktéž v hlavičce, v proměnné dwXfrmBlockSize. Jsou známy následující identifikátory formátů:
V následujících odstavcích jsou popsány známé patchovací metody.
Data pro aktualizaci začínají 32-bitovou hodnotou, která obsahuje velikost dat po rozbalení. Následují data BSDIFF40, která jsou zkomprimovaná metodou RLE. Formát modifikované verze BSDIFF40 použité v MPQ je následující:
Pozice | Velikost | Význam |
---|---|---|
0x0000 | 0x08 bytů | Signatura 'BSDIFF40' |
0x0008 | 0x08 bytů | Velikost bloku CTRL (v bytech) |
0x0010 | 0x08 bytů | Velikost bloku DATA (v bytech) |
0x0018 | 0x08 bytů | Velikost souboru po aplikaci aktualizace (v bytech) |
0x0020 | Proměnná | Blok CTRL. Skládá se z pole trojic, každá trojice má velikost 0x0C bytů (3 DWORDS). Velikost bloku CTRL je uložena v hlavičce BSDIFF. Toto je rozdíl oproti původní verzi BSDIFF40, která používá 64 bitů pro každou hodnotu z trojice. |
Proměnná | Proměnná | Blok DATA. Délka bloku je uložena v hlavičce BSDIFF. |
Proměnná | Proměnná | Blok EXTRA. Začátek je za blokem DATA, a blok sahá až na konec dat BSDIFF. |
Jak název říká, typ COPY plně nahrazuje původní soubor.
void Decompress_RLE(LPBYTE pbDecompressed, DWORD cbDecompressed, LPBYTE pbCompressed, DWORD cbCompressed) { LPBYTE pbDecompressedEnd = pbDecompressed + cbDecompressed; LPBYTE pbCompressedEnd = pbCompressed + cbCompressed; BYTE RepeatCount; BYTE OneByte; // Cut the initial DWORD from the compressed chunk pbCompressed += sizeof(DWORD); cbCompressed -= sizeof(DWORD); // Pre-fill decompressed buffer with zeros memset(pbDecompressed, 0, cbDecompressed); // Unpack while(pbCompressed < pbCompressedEnd) { OneByte = *pbCompressed++; // Is it a repetition byte ? if(OneByte & 0x80) { RepeatCount = (OneByte & 0x7F) + 1; for(BYTE i = 0; i < RepeatCount; i++) { if(pbDecompressed == pbDecompressedEnd || pbCompressed == pbCompressedEnd) break; *pbDecompressed++ = *pbCompressed++; } } else { pbDecompressed += (OneByte + 1); } } }
Více informací o aplikaci aktualizace typu BSD0 najdete ve zdrojovém kódu knihovny StormLib, ve funkci ApplyMpqPatch_BSD0()
Copyright (c) Ladislav Zezula 2010