Archivy MPQ
Neúplné archivy
Neúplné soubory umožňují mít na disku pouze malou část většího, existujícího souboru, a chybějící části stáhnout až v okamžiku, kdy jsou potřeba. Formát neúplných souborů není vázán na archivy MPQ, ale slouží jako médium pro ukládání obsahu archivu. Neúplné soubory mohou být použity pro jakýkoliv typ souboru nebo archivu.
Neúplné soubory byly poprvé použíty ve zkušební verzi hry World of Warcraft. Protože množství dat použitých ve hře je obrovské, trvalo by neúnosně dlouhou dobu stáhnout celou hru. Pro zkrácení doby stahování je vytvořen neúplný archiv, a chybějící části jsou dotaženy podle potřeby, jak hráč postupuje hlouběji do hry.
Neúplné archivy MPQ mají obvykle dvojitou příponu .MPQ.part, např. interface.MPQ.part.
Obecná struktura neúplného souboru je tato:
Hlavička neúplného souboru použítá společností Blizzard obsahuje signaturu, číslo sestavení hry, velikost jedné části souboru a celkovou velikost souboru. Hlavička neúplného souboru je popsána následující strukturou v jazyce C:
// Structure describing the PART file header typedef struct _PART_FILE_HEADER { // Always set to 2 DWORD PartialVersion; // Game build number as ASCIIZ string char GameBuildNumber[8]; // Unknown DWORD Unknown0C; // Unknown DWORD Unknown10; // Seems to contain 0x1C, which is the size of the rest of the header DWORD Unknown14; // Unknown DWORD Unknown18; // Unknown DWORD Unknown1C; // Unknown DWORD Unknown20; // Seems to always be zero DWORD ZeroValue; // Low 32 bits of the file size DWORD FileSizeLo; // High 32 bits of the file size DWORD FileSizeHi; // Size of one file part, in bytes DWORD PartSize; } PART_FILE_HEADER, *PPART_FILE_HEADER;
Důležitými členy struktury jsou FileSizeLo, FileSizeHi a PartSize. Tyto jsou použity pro vypočítání počtu částí na které je archiv MPQ rozdělen. Jsou také použity ke spočítání vypočítání počtu položek v mapě částí, která následuje za hlavičkou.
Mapa částí souboru následuje za hlavičkou neúplného souboru. Každá část má odpovídající položku v mapě částí. Celkový počet položek v mapě je spočítán podle vzorce:
PartCount = (DWORD)((FullFileSize + PartSize - 1) / PartSize)
kde FullFileSize a PartSize jsou uloženy v hlavičce neúplného souboru.
Položka mapy je popsána následující strukturou v jazyce C:
// Structure describing the entry in the PART map typedef struct _PART_FILE_MAP_ENRY { // 3 = the part in present in the file DWORD Flags; // Low 32 bits of the part position in the file DWORD BlockOffsLo; // High 32 bits of the part position in the file DWORD BlockOffsHi; // Unknown DWORD Unknown0C; // Unknown DWORD Unknown10; } PART_FILE_MAP_ENRY, *PPART_FILE_MAP_ENRY;
Za mapou částí souboru následují bloky dat. Každý blok představuje část vlastního souboru, jako např. archivy MPQ. Velikost každé části je uložen v hlavičce neúplného souboru, s vyjímkou poslední části, která může mít velikost menší. Části souboru jsou uloženy v pořadí, v jakém byly načteny, a nemusejí následovat jedna za druhou.
Následující příklad demonstruje čtení hlavičky MPQ z neúplného souboru. Pro ujasnění používá termín "virtuální pozice" pro aktuální pozici v uložených datech a "pozice v neúplném souboru" pro označení pozice v souboru na disku. Předpokládáme, že pozice hlavičky archivu MPQ je na virtuální pozici 0. Příklad je napsán pro platformu Win32.
LARGE_INTEGER VirtualOffset = {0}; LARGE_INTEGER RawFileOffset; DWORD PartIndex; // Verify if the virtual offset doesn't go beyond the virtual file size if(VirtualOffset.QuadPart >= VirtualSize.QuadPart) { SetLastError(ERROR_HANDLE_EOF); return false; } // Calculate the part index for the given virtual offset PartIndex = (DWORD)(VirtualOffset.QuadPart / PartSize); // Check if that part is available in the file if(PartMap[PartIndex].Flags != 3) { SetLastError(ERROR_CAN_NOT_COMPLETE); return false; } // Calculate the offset of data within the file part PartOffset = VirtualOffset.LowPart & (PartSize - 1); // Calculate raw file offset RawFileOffset.HighPart = PartMap[PartIndex].BlockOffsHi; RawFileOffset.LowPart = PartMap[PartIndex].BlockOffsLo; RawFileOffset.QuadPart += PartOffset; // Read the data from the file. For simplicity, we will not check bounds SetFilePointer(hPartFile, RawFileOffset.LowPart, &RawFileOffset.HighPart, FILE_BEGIN); ReadFile(hPartFile, pvBuffer, dwBumberOfBytesToRead, &dwNumberOfBytesRead, NULL);
Copyright (c) Ladislav Zezula 2010