Archivy MPQ
Knihovna StormLib
Po vydání hry Diablo, mnoho hráčů hledalo nástroj, který by umožnil přehrávat hudbu a zvuky ze hry. Krátce poté se začaly objevovat nástroje, které používaly knihovnu Storm.dll (součást hry) ke čtení dat z archivů hry. Společnost Blizzard si toho byla vědoma a od verze 1.04 obsahovala knihovna Storm.dll triky pro zabránění snadného použití ke čtení Blizzardovských archivů. Přestože tyto triky jsou snadno překonatelné, komunita hledala alternativu pro čtení a vytváření archivů MPQ bez pomoci Blizzardí knihovny.
První knihovna, ktera umožňovala vytváření archivů MPQ, byla LMPQAPI, vytvořena ruským programátorem Andrejem Lelikovem. Tato knihovna interně používala StarEdit.exe a umožňovala volat interní funkce, které uměly vytvářet archivy MPQ a přidávat soubory.
Tom Amigo byl první, kdo uveřejnil Stormless MPQ Editor, který uměl číst (a později i zapisovat) archivy MPQ bez použití Storm.dll. Zhruba ve stejné době (rok 2000) jsem začal reverzovat Storm.dll, abych získal funkční kód napsaný v C++, ktery umí číst archivy MPQ. Výsledkem byla knihovna StormLib v 1.0. Ke dnešnímu dni (rok 2010) je knihovna pořád udržovaná, a umí číst a přidávat soubory do archivů MPQ.
Knihovna StormLib je skupina modulů, napsaných v C++, které umí číst a také zapisovat soubory z archivů MPQ. Původní verze byla napsaná pro platformu Win32. Existuje i verze pro Linux, napsaná Markem Friedemannem a verze pro Mac, kterou napsal Sam Wilkins. Knihovna je volně ke stažení, a k použití ve vlastních projektech není třeba licence. Stáhnout se dá v sekci Ke stažení. Pokud byste našli jakoukoliv chybu, nebo odhalili něco nefunkčního, dejte mi prosím vědět, a také mi pošlete archiv MPQ, který problém způsobil.
Veškeré potřebné funkce jsou k dispozici v hlavičkovém souboru StormLib.h. Dokumentaci je také možné stáhnout.
| Funkce | Popis |
|---|---|
| SFileOpenArchive | Otevře archiv MPQ |
| SFileCreateArchive | Vytvoří nový archiv MPQ |
| SFileCreateArchiveEx | Funkce odstraněna |
| SFileAddListFile | Přidá další listfile do otevřeného archivu MPQ pro zlepšení vyhledávání |
| SFileSetLocale | Změní nastavení jazyka pro přidávání nových souborů |
| SFileGetLocale | Vrací aktuální jazyk nově přidávaných souborů |
| SFileFlushArchive | Uloží všechna data MPQ archivu na disk |
| SFileCloseArchive | Uzavře archiv MPQ |
| SFileSetMaxFileCount | Změní limit pro počet souborů v archivu |
| SFileCompactArchive | Defragmentuje (přebalí) celý archiv. Slouží k uvolnění všech mezer, které vznikly ukládáním do archivu |
| SFileSetCompactCallback | Umožňuje nastavit callback funkci, která informuje o průběhu defragmentace archivu MPQ |
| Funkce | Popis |
|---|---|
| SFileOpenPatchArchive | Přidá aktualizaci k otevřenému archivu |
| SFileIsPatchedArchive | Zjistí, zda otevřený archiv obsahuje aktualizace |
| Funkce | Popis |
|---|---|
| SFileOpenFileEx | Otevře soubor v archivu MPQ |
| SFileGetFileSize | Vrací velikost souboru v MPQ archivu |
| SFileSetFilePointer | Nastaví nový ukazatel pozice v souboru. |
| SFileReadFile | Čte data ze souboru |
| SFileCloseFile | Uzavře soubor |
| SFileHasFile | Rychlé ověření, zda soubor v archivu existuje |
| SFileGetFileName | Zjistí jméno otevřeného souboru |
| SFileGetFileInfo | Zjistí jméno otevřeného souboru |
| SFileVerifyFile | Ověří integritu souboru oproti rozšířeným atributům |
| SFileVerifyArchive | Ověří digitální podpis archivu |
| SFileExtractFile | Vybalí soubor z archivu MPQ |
| Funkce | Popis |
|---|---|
| SFileFindFirstFile | Najde první soubor, který vyhovuje specifikaci |
| SFileFindNextFile | Najde další soubor, který vyhovuje specifikaci |
| SFileFindClose | Ukončí hledání v MPQ |
| SListFileFindFirstFile | Najde první soubor v listfile, který vyhovuje specifikaci |
| SListFileFindNextFile | Najde další soubor v listfile, který vyhovuje specifikaci |
| SListFileFindClose | Ukončí hledání v listfile |
| SFileEnumLocales | Enumeruje všechny jazykové verze daného souboru |
| Funkce | Popis |
|---|---|
| SFileCreateFile | Vytvoří nový soubor v archivu a připraví jej pro zápis dat |
| SFileWriteFile | Zapíše data do souboru v archivu |
| SFileFinishFile | Dokončí vytváření souboru v archivu |
| SFileAddFileEx | Přidá soubor do archivu |
| SFileAddFile | Přidá datový soubor do archivu (zastaralá funkce) |
| SFileAddWave | Přidá zvukový soubor VAWE do archivu (zastaralá funkce) |
| SFileRemoveFile | Odstraní soubor z archivu MPQ |
| SFileRenameFile | Přejmenuje soubor v archivu MPQ |
| SFileSetFileLocale | Změní jazyk souboru v archivu |
| SFileSetDataCompression | Nastaví kompresní metodu pro soubory přidávané pomocí SFileAddFile |
| SFileSetAddFileCallback | Nastaví ukazatel na funkci, pomocí které je aplikace informována o postupu přidávání souboru do MPQ |
| Funkce | Popis |
|---|---|
| SCompImplode | Komprimuje blok dat metodou IMPLODE (Pkware Data Compression Library) |
| SCompExplode | Dekomprimuje block dat zkomprimovaný funkcí SCompImplode |
| SCompCompress | Komprimuje blok dat použitím některé z kompresních metod použitých v MPQ |
| SCompDecompress | Dekomprimuje blok dat zkomprimovaný funkcí SCompCompress |
Zde je příklad funkce, která extrahuje jeden soubor z archivu MPQ.
//-----------------------------------------------------------------------------
// Extracts an archived file and saves it to the disk.
//
// Parameters :
//
// char * szArchiveName - Archive file name
// char * szArchivedFile - Name/number of archived file.
// char * szFileName - Name of the target disk file.
static int ExtractFile(char * szArchiveName, char * szArchivedFile, char * szFileName)
{
HANDLE hMpq = NULL; // Open archive handle
HANDLE hFile = NULL; // Archived file handle
HANDLE handle = NULL; // Disk file handle
int nError = ERROR_SUCCESS; // Result value
// Open an archive, e.g. "d2music.mpq"
if(nError == ERROR_SUCCESS)
{
if(!SFileOpenArchive(szArchiveName, 0, 0, &hMpq))
nError = GetLastError();
}
// Open a file in the archive, e.g. "data\global\music\Act1\tristram.wav"
if(nError == ERROR_SUCCESS)
{
if(!SFileOpenFileEx(hMpq, szArchivedFile, 0, &hFile))
nError = GetLastError()
}
// Create the target file
if(nError == ERROR_SUCCESS)
{
handle = CreateFile(szFileName, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, 0, NULL);
if(handle == INVALID_HANDLE_VALUE)
nError = GetLastError();
}
// Read the file from the archive
if(nError == ERROR_SUCCESS)
{
char szBuffer[0x10000];
DWORD dwBytes = 1;
while(dwBytes > 0)
{
SFileReadFile(hFile, szBuffer, sizeof(szBuffer), &dwBytes, NULL);
if(dwBytes > 0)
WriteFile(handle, szBuffer, dwBytes, &dwBytes, NULL);
}
}
// Cleanup and exit
if(handle != NULL)
CloseHandle(handle);
if(hFile != NULL)
SFileCloseFile(hFile);
if(hMpq != NULL)
SFileCloseArchive(hMpq);
return nError;
}
Copyright (c) Ladislav Zezula 2003 - 2010