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