MPQ Archives

The StormLib library

History

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

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.

StormLib funtions

All StormLib functions are defined in StormLib.h header file. You can also download the entire documentation for offline browsing.

Manipulating MPQ archives

Function Description
SFileOpenArchive Opens a MPQ archive
SFileCreateArchive Creates a new MPQ archive
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
SFileCompactArchive Compacts (rebuilds) the archive, freeing all gaps that were created by write operations
SFileSetCompactCallback Allows to set a callback function for archive compacting

Using patched archives

Function Description
SFileOpenPatchArchive Adds a patch archive for an existing open archive
SFileIsPatchedArchive Determines if the open MPQ has patches

Reading files

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

File searching

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

Adding files to MPQ

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

Compression functions

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

Example

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;
}