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