MPQ Archives
The StormLib library
When Diablo I was released, many people were looking for a way how to play the music and sounds from the game. Soon, tools started to appear, that used Storm.dll library (shipped with the game) for reading data from the game MPQs. Blizzard was aware of that and since version 1.04 of Diablo, Storm.dll contained tricks to prevent people from using it to read game archives. Althought these tricks can be overcame, people started to look for an alternative how to read (and also to write) MPQ archives without relying on Blizzard library.
The first library that was able to write MPQ archives was LMPQAPI, created by Russian programmer Andrej Lelikov. The library utilized StarEdit.exe and allowed to call internal functions that were able to create MPQs and add files to them.
Tom Amigo was the first guy who released a Stormless MPQ Editor, which was able to read (and also write later) MPQ archives without using Storm.dll. At about the same time, (year 2000), I started to reverse Storm.dll in order to get a working, public C++ code that is able to read MPQ archives. As a result, StormLib v 1.0 was released. As of today (year 2010), StormLib is still being maintained and is currently able to read files and add files to MPQ archives.
The StormLib library is a pack of modules, written in C++, which are able to read and also to write files from/to the MPQ archives. The original version was written for the Win32 platform. There is also a Linux port, made by Marko Friedemann and a Mac port, made by Sam Wilkins. The library is free, no license is needed to use it in your projects. You can download it from the Downloads section. Should you find any problems or or malfunctions, please, let me know and also send me the MPQ archive that caused the problem.
All StormLib functions are defined in StormLib.h header file. You can also download the entire documentation for offline browsing.
Function | Description |
---|---|
SFileOpenArchive | Opens a MPQ archive |
SFileCreateArchive | Creates a new MPQ archive |
SFileCreateArchive2 | Creates a new MPQ archive. Gives more options than SFileCreateArchive |
SFileCreateArchiveEx | Removed |
SFileAddListFile | Adds another list file to the open archive in order to improve searching |
SFileSetLocale | Changes default locale ID for adding new files |
SFileGetLocale | Returns current locale ID for adding new files |
SFileFlushArchive | Flushes all unsaved data to the disk |
SFileCloseArchive | Closes an open archive |
SFileSetMaxFileCount | Changes the file limit for the archive |
SFileSignArchive | Signs the archive |
SFileCompactArchive | Compacts (rebuilds) the archive, freeing all gaps that were created by write operations |
SFileSetCompactCallback | Allows to set a callback function for archive compacting |
Function | Description |
---|---|
SFileOpenPatchArchive | Adds a patch archive for an existing open archive |
SFileIsPatchedArchive | Determines if the open MPQ has patches |
Function | Description |
---|---|
SFileOpenFileEx | Opens a file from MPQ archive |
SFileGetFileSize | Retrieves a size of the file within archive |
SFileSetFilePointer | Sets a new position within archive file |
SFileReadFile | Reads data from the file |
SFileCloseFile | Closes an open file |
SFileHasFile | Quick check if the file exists within MPQ archive, without opening it |
SFileGetFileName | Retrieves name of an open file |
SFileGetFileInfo | Retrieves an information about open file or archive |
SFileVerifyFile | Verifies a file against its extended attributes |
SFileVerifyArchive | Verifies the digital signature of an archive |
SFileExtractFile | Extracts a file from MPQ to the local drive |
Function | Description |
---|---|
SFileFindFirstFile | Finds a first file matching the specification |
SFileFindNextFile | Finds a next file matching the specification |
SFileFindClose | Stops searching in MPQ |
SListFileFindFirstFile | Finds a first line in the listfile that matches the specification |
SListFileFindNextFile | Finds a next line in the listfile that matches the specification |
SListFileFindClose | Stops searching in the listfile |
SFileEnumLocales | Enumerates all locales for a given file that are in the archive |
Function | Description |
---|---|
SFileCreateFile | Creates a new file in MPQ and prepares it for writing data |
SFileWriteFile | Writes data to the file within MPQ |
SFileFinishFile | Finalizes writing file to the MPQ |
SFileAddFileEx | Adds a file to the archive |
SFileAddFile | Adds a data file to the archive (obsolete) |
SFileAddWave | Adds a WAVE file to the archive (obsolete) |
SFileRemoveFile | Deletes a file from MPQ archive |
SFileRenameFile | Renames a file within MPQ archive |
SFileSetFileLocale | Changes locale of a file in MPQ archive |
SFileSetDataCompression | Sets default compression method for adding a data file using SFileAddFile |
SFileSetAddFileCallback | Sets callback function that is called to inform the calling application about progress of adding file to the archive |
Function | Description |
---|---|
SCompImplode | Compresses a data buffer using IMPLODE method (Pkware Data Compression Library) |
SCompExplode | Decompresses a buffer that has been imploded by SCompImplode |
SCompCompress | Compresses a data buffer using any of the supported MPQ compressions |
SCompDecompress | Decompresses a data buffer that has been compressed by SCompCompress |
Here is an example of a function, which extracts one file from the archive.
//----------------------------------------------------------------------------- // 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