C H A P T E RF O U R

THE STARCRAFT CAMPAIGN EDITOR AND THE MPQ API LIBRARY

Just what is the Starcraft Campaign Editor (or StarEdit, for short)? Just about anyone who owns Starcraft can tell you that it is a program that allows you to make Starcraft missions, and save them to disk in the form of SCM or SCX files. But is that really all it is? Are these SCM/SCXs nothing more than raw data files, like they were in Warcraft 2? Well, anyone who has ever looked at an SCM/SCX in a hex editor can tell you a different answer. As a matter of fact, SCM/SCXs are actually MPQs! The reasons for this should be immediately obvious: for faster distribution over Battle.net with compression, and to allow the sounds you put in a level to easily be packed right into the SCM/SCX itself. But what does all this mean to us?  It means that StarEdit contains the elusive MPQ-writing routines we've been unable to find in Storm.

Using StarEdit - The MPQ API Library

WARNING: THE REST OF THIS CHAPTER IS SPECIFIC TO THE WINDOWS PLATFORM, AND APPLIES ONLY TO THE MPQ API LIBRARY VERSION 2.0 OR GREATER!

Click here to download the MPQ API Library for Windows

But not so quick to fire up your compilers, there. Just because StarEdit has the functions doesn't mean we can use them. In fact, it couldn't really be more difficult to use them. You see, unlike shared libraries like Storm, StarEdit's internal functions are protected by complicated OS mechanisms which are well beyond the scope of this document. Consequently, it is totally impossible for a normal (or even good) programmer to get access to them. For this task, none other than a programmer who can manipulate the internal workings of the operating system will do. This is where Andrey Lelikov (commonly referred to as Lelik) enters the picture.
Lelik is a (very) experienced Russian programmer who is no stranger to the internal workings of operating system. He has engineered an ingenious - if however unorthodox - method of using the StarEdit MPQ functions. Once again, I won't confuse you with the details, as you probably wouldn't understand them anyway (I've seen the source code); suffice to say that Lelik "possesses" StarEdit, and makes it call its own functions (aren't you sorry you asked?). Fortunately, Lelik has neatly packaged his skills into something called the MPQ API Library (as a matter of fact, I've had the privilege of working with Lelik on actually writing the most recent version of the Library).
Like Storm, the MPQ API Library (aka LMPQAPI) is contained in a shared library (unfortunately, like the Storm Interface Library, no Mac version of LMPQAPI exists at this time). LMPQAPI not only encapsulates the interface to access the MPQ writing functions in StarEdit, but also an interface to access Storm. That means that if you want to use both Storm and StarEdit, you don't need to use both LMPQAPI and the Storm Interface Library; you only need LMPQAPI.
Okay, now I have just one more thing to mention here. The way you can differentiate between the Storm and StarEdit functions when using LMPQAPI is this: Storm functions have a prefix of 'SFile' (just like when using the Storm Interface Library), while StarEdit functions have a prefix of 'Mpq'. This is important to know for one reason: Storm and StarEdit functions are not compatible. That means that you may not obtain an MPQ HANDLE with SFileOpenArchive and then call a function in StarEdit (an 'Mpq' prefixed function) with that HANDLE, or vice versa. If you do this, the call will fail, or maybe even crash. Now you know.

¿Sé Habla Español?

Because 75% or more of the Starcraft/Diablo players are English speaking, most MPQs are developed and tested on the English version of these games. To any of these people, any home-brewed MPQ would work fine. As a matter of fact, since 98% of the files in the standard MPQs are language-neutral (files such as images, etc.), even someone using the same MPQ with a non-English version of the game would stand a good chance of never noticing anything unusual. But, obvious or not, there are some problems here, and it's only a matter of time before someone notices them.
As explained in chapters 2 and 3, the MPQ format contains robust multilingual features. But, in case you haven't noticed, there is no real need to create multilingual SCM/SCXs. That means that it isn't necessary for StarEdit to support the multilingual features; and, as infamously lazy as Blizzard programmers are, StarEdit doesn't support these features. But, we are interested in working with common MPQs, where multilingualism is useful, if not essential. Nevertheless, there is no getting around the fact StarEdit simply can't do it. Technically, all of the StarEdit functions only work with files that have a language code of 0, the language-neutral code (see chapter 3). So that means that MpqAddFileToArchive and MpqAddWAVToArchive will only add language- neutral files, MpqDeleteFile only deletes language-neutral files, and MpqRenameFile only renames language-neutral files.
The consequences of this design decision are not immediately obvious. Also mentioned in chapter 3 is the fact that Storm uses a language filter set by SFileSetLocale to decide which file to open if there are multiple ones with the same name in different languages. Suppose you have an MPQ that is replacing patch_rt.mpq in Starcraft, and in that MPQ is an English/language-neutral version of the file rez\gluAll.tbl (this is one of the files that has multiple language versions) but not a Portuguese (language chosen completely arbitrarily) version. When you run this thing on a Portuguese version of Starcraft, it will check for the Portuguese version in your MPQ, fail, and default to the English version, right? Well, no. You see, Storm allows you to open several MPQs at one time and let Storm search through all the loaded MPQs for a file and load it from the newest MPQ (whichever one it may be) automatically. But, in doing this, Storm checks all the open MPQs for a specific language before it defaults to the language-neutral one. That means that, in the previous scenario, Storm will load the Portuguese version of the file from broodat.mpq instead of your own language-neutral version! Unfortunately, at this time there is no way around this problem. I hope to remedy this problem in an upcoming version of LMPQAPI.

Initializing the MPQ API Library - MpqInitialize
BOOL WINAPI MpqInitialize();

Unlike Storm, LMPQAPI has an enormous, complicated task to perform in taking the reins of StarEdit. Consequently, it is not possible for LMPQAPI to do all of this at startup. You must tell LMPQAPI when it's time to go to work. Fortunately, this is a simple task; all you must do is call MpqInitialize at the beginning of your program, and LMPQAPI will do the rest. But, it is imperative that you do indeed call MpqInitialize near the beginning of you program, and definitely before you make any other calls to LMPQAPI. Also, MpqInitialize must be called before you make any Storm calls, even though you didn't have to do anything in the Storm Interface Library (one call to MpqInitialize is all it takes to initialize both Storm and StarEdit).

  1. Either Starcraft/Brood War 1.07 must be installed properly on the computer or the Storm.dll and StarEdit.exe files from Starcraft/Brood War 1.07 must be in the directory of your program.
  2. StarEdit must not be running at the same time LMPQAPI is.

The two requirements shown above must be met in order for MpqInitialize to succeed, and, one of these conditions not being met is by far the most common reason for MpqInitialize to fail (although in rare cases it may do so for some other reason). Should MpqInitialize fail for one reason or another, it will return FALSE, and usually set an error value you can retrieve with GetLastError. If LMPQAPI cannot find the StarEdit.exe file in the Starcraft directory or in the same directory as your program, GetLastError will return MPQ_ERROR_NO_STAREDIT. If the version of StarEdit.exe LMPQAPI finds is not that from Starcraft/Brood War 1.07, GetLastError will return MPQ_ERROR_BAD_STAREDIT. If any version of StarEdit is running when you call MpqInitialize, GetLastError will return MPQ_ERROR_STAREDIT_RUNNING. If MpqInitialize should fail for any other reason, GetLastError will usually return MPQ_ERROR_INIT_FAILED. But, regardless of why MpqInitialize failed or what GetLastError returns, it is very important that you end your program as quickly as possible; and whatever you do, do not call any other LMPQAPI functions (Storm or StarEdit functions); that includes trying to call MpqInitialize again.

Opening an MPQ for Editing - MpqOpenArchiveForUpdate
HANDLE WINAPI MpqOpenArchiveForUpdate(LPCSTR lpFileName, DWORD dwCreationDisposition, DWORD dwHashTableSize);
 
Parameter What it is
 lpFileName [in] A pointer to NULL-terminated string that holds the path of the MPQ to open. MpqOpenArchiveForUpdate will fail if this is NULL.
 dwCreationDisposition [in] Specifies what MpqOpenArchiveForUpdate should do with the archive if it does/doesn't exist. Must be one of the following values defined in lmpqapi.h:
MOAU_CREATE_NEW MpqOpenArchiveForUpdate will create a brand new archive. If lpFileName already exists, MpqOpenArchiveForUpdate will fail.
MOAU_CREATE_ALWAYS MpqOpenArchiveForUpdate will create a new archive if lpFileName doesn't exist. If lpFileName does exist, it will be deleted and overwritten.
MOAU_OPEN_EXISTING MpqOpenArchiveForUpdate will open the archive lpFileName. MpqOpenArchiveForUpdate will fail if lpFileName does not exist.
MOAU_OPEN_ALWAYS If lpFileName exists, MpqOpenArchiveForUpdate will open it. If it doesn't, MpqOpenArchiveForUpdate will create a new archive.
MOAU_MAINTAIN_LISTFILE This flag specifies that as MpqOpenArchiveForUpdate uses the archive lpFileName, it should update the internal listfile, if one exists. This is a flag, and must be ORed (with the | operator) with one of the other options for dwCreationDisposition
 dwHashTableSize [in] Whenever MpqOpenArchiveForUpdate creates a new archive (see the dwCreationDisposition for details on when this occurs), dwHashTableSize specifies how large the hash table for the new archive will be, with a minimum size of 16, and a maximum size of 262,144 (if dwHashTableSize is not within these values, MpqOpenArchiveForUpdate will change it). This parameter does not affect archives that already exist.

As with Storm, before you can start using an MPQ archive, you must open it. MpqOpenArchiveForUpdate opens (or creates) an archive for use with the other StarEdit functions, and gives a HANDLE to that archive as its return value. But unlike SFileOpenArchive, MpqOpenArchiveForUpdate requires that you make some choices ahead of time. The first choice is the dwCreationDisposition parameter. It tells MpqOpenArchiveForUpdate whether it should create a new archive, open an existing one, or something in-between. The other decisive parameter is dwHashTableSize. dwHashTableSize tells MpqOpenArchiveForUpdate what size the archive's hash table (which is also the file limit) should be, in the event that MpqOpenArchiveForUpdate must create a new archive. 1000 is generally a good size for an archive's hash table unless you know that the archive will definitely need to hold more than 1000 files. But also keep in mind that each hash table entry will add 16 bytes to the archive's base size (see chapter 2 for general information about hash tables, or chapter 5 for more info about the MPQ hash table).
As with nearly everything, MpqOpenArchiveForUpdate fails on occasion. If this happens, MpqOpenArchiveForUpdate's return value will be either INVALID_HANDLE_VALUE or NULL. We can often (but not always) get helpful information about what happened by calling GetLastError. If the lpFileName parameter is NULL, GetLastError will return ERROR_INVALID_PARAMETER. If dwCreationDisposition is MOAU_OPEN_EXISTING and the file lpFileName does not exist, GetLastError will return ERROR_FILE_NOT_FOUND. Conversely, if dwCreationDisposition is MOAU_CREATE_NEW and the file lpFileName already exists, GetLastError will return ERROR_ALREADY_EXISTS. Finally, if the MPQ archive exists but is invalid or corrupted, GetLastError will return MPQ_ERROR_MPQ_INVALID. On some rare occasions, GetLastError will return some other, cryptic error code.

Closing a Modified Archive - MpqCloseUpdatedArchive
BOOL WINAPI MpqCloseUpdatedArchive(HANDLE hMPQ, DWORD dwUnknown);
 
Parameter What it is
 hMPQ [in] The HANDLE of the MPQ to close, which was acquired earlier with MpqOpenArchiveForUpdate. MpqCloseUpdatedArchive will fail (or worse) if this is NULL or a HANDLE not obtained with MpqOpenArchiveForUpdate.
 dwUnknown Unknown. Should always be NULL.

Just like in Storm, where any MPQ you open with SFileOpenArchive must be closed with SFileCloseArchive when you're done with it; an MPQ archive opened with MpqOpenArchiveForUpdate must be closed with MpqCloseUpdatedArchive. However, in this case things are a little more dire. You see, Storm doesn't modify the actual MPQ, so nothing special needs to be done to close a MPQ HANDLE. But, StarEdit does modify the MPQ. And, as a matter of fact, an MPQ's hash and file tables (see chapters 2 and 5) don't actually get written to the MPQ on disk until the MPQ HANDLE is closed with MpqCloseUpdatedArchive. That means it's very important to close StarEdit MPQ HANDLEs quickly, or you risk corrupting the MPQ (this is especially true in the case of a crash).

Adding a File - MpqAddFileToArchive
BOOL WINAPI MpqAddFileToArchive(HANDLE hMPQ, LPCSTR lpSourceFileName, LPCSTR lpDestFileName, DWORD dwFlags);
Parameter What it is
 hMPQ [in] The HANDLE of the MPQ to add the file to, which was acquired earlier with MpqOpenArchiveForUpdate. MpqAddFileToArchive will fail if this is NULL or a HANDLE not obtained with MpqOpenArchiveForUpdate.
 lpSourceFileName [in] A pointer to a NULL-terminated string containing the path of the file on disk to add. MpqAddFileToArchive will crash if this is NULL.
 lpDestFileName [in] A pointer to a NULL-terminated string containing the name that the file will be given in the MPQ. MpqAddFileToArchive will crash if this is NULL.
 dwFlags [in] Flags specifying properties that MpqAddFileToArchive will apply to the file inside the MPQ. Must be a combination of the following flags specified in lmpqapi.h:
MAFA_ENCRYPT The file will be encrypted.
MAFA_COMPRESS The file will be compressed.
MAFA_REPLACE_EXISTING If the file lpDestFileName already exists in the MPQ, it will be replaced with the new file to be added

Invariably, about 95% of the time you'll use the StarEdit MPQ functions for no other reason than to add files to an MPQ. For this task, you'll use MpqAddFileToArchive and its sister function MpqAddWAVToArchive (discussed later). MpqAddFileToArchive adds the file lpSourceFileName on disk to the MPQ hMPQ with a name of lpDestFileName, compressing and/or encrypting it in the process.
However, there are a number of design oversights in MpqAddFileToArchive which can quickly become major trouble spots if you're not ready for them. First of all, MpqAddFileToArchive does not check to make sure that lpSourceFileName and lpDestFileName are not NULL. That means that MpqAddFileToArchive may crash if either of the parameters are NULL. Next is the way MpqAddFileToArchive handles things when you overwrite an existing file in the MPQ. You see, when you call MpqAddFileToArchive to add a file that already exists in the MPQ (in which case you would have to specify MAFA_REPLACE_EXISTING in dwFlags), MpqAddFileToArchive will indiscriminately delete the existing file lpDestFileName before it makes sure that the file lpSourceFileName even exists. It doesn't take a rocket-scientist to figure out the serious problem this flaw could cause. Fortunately, the solution to both these problems is simple and easy: don't leave it up to chance; make sure lpSourceFileName and lpDestFileName are valid (non-NULL), and that lpSourceFileName exists before calling MpqAddFileToArchive.
Now, should you make it past these and all the other hurdles and MpqAddFileToArchive is able to successfully add the file, MpqAddFileToArchive will gives you the thumbs-up with a return value of TRUE. Or, if something went wrong, it will return FALSE. In this case, a small amount of information can be obtained by calling GetLastError. If the file lpSourceFileName doesn't exist, GetLastError will return ERROR_FILE_NOT_FOUND (although it's a little late now!). If the hash table is full (see chapter 2), GetLastError will return MPQ_ERROR_HASH_TABLE_FULL. If the file lpDestFileName already exists and MAFA_REPLACE_EXISTING was not specified in dwFlags, GetLastError will return MPQ_ERROR_ALREADY_EXISTS. But, on many occasions, GetLastError will return some other, cryptic error code or no code at all.

Adding a File with WAV Compression - MpqAddWAVToArchive
BOOL WINAPI MpqAddWAVToArchive(HANDLE hMPQ, LPCSTR lpSourceFileName, LPCSTR lpDestFileName, DWORD dwFlags, DWORD dwQuality);
Parameter What it is
 hMPQ [in] The HANDLE of the MPQ to add the file to, which was acquired earlier with MpqOpenArchiveForUpdate. MpqAddWAVToArchive will fail if this is NULL or a HANDLE not obtained with MpqOpenArchiveForUpdate.
 lpSourceFileName [in] A pointer to a NULL-terminated string containing the path of the file on disk to add. MpqAddWAVToArchive will crash if this is NULL.
 lpDestFileName [in] A pointer to a NULL-terminated string containing the name that the file will be given in the MPQ. MpqAddWAVToArchive will crash if this is NULL.
 dwFlags [in] Flags specifying properties that MpqAddWAVToArchive will apply to the file inside the MPQ. Must be a combination of the following flags specified in lmpqapi.h:
MAFA_ENCRYPT The file will be encrypted.
MAFA_REPLACE_EXISTING If the file lpDestFileName already exists in the MPQ, it will be replaced with the new file to be added
 dwQuality [in] Specifies the quality the WAV file will be compressed to. Must be one of the following values defined in lmpqapi.h:
MAWA_QUALITY_HIGH Best sound quality, least compression. WAV lpDestFileName in MPQ will occupy the most space after compression (but still less space than if you had added the file with MpqAddFileToArchive instead)
MAWA_QUALITY_MEDIUM Medium sound quality, medium compression. Balance between quality and compression.
MAWA_QUALITY_LOW Highest compression, worst sound quality. WAV lpDestFileName in MPQ will occupy the least space after compression.

Undoubtedly, the most popular new feature of the LMPQAPI v2.0 (the one that I worked on!) would have to be this function right here: MpqAddWAVToArchive. You see, while MpqAddFileToArchive's standard compression works fine for about 80% of files (non-WAV files), it chokes on WAV files, compressing them, on average, about 5%. This is due to the nature of WAV data and its fundamental incompressibility. Here, a special type of compression is necessary: WAV compression. MpqAddWAVToArchive supplies this special compression type.
Despite the extreme differences in the way they function, the interface to MpqAddWAVToArchive is almost identical to that of MpqAddFileToArchive, the only difference being a single new parameter: dwQuality. This parameter specifies the quality the finished WAV will have after being compressed. You see, unlike MpqAddFileToArchive's standard compression, MpqAddWAVToArchive's WAV compression actually degrades the WAV. That means the WAV after compression will be of lower quality than the original WAV before compression. How much lower quality is dependant on dwQuality. If you have a music WAV and music quality is important to you, you'll probably want to use MAWA_QUALITY_HIGH, as it best preserves the WAV; whereas with a voice WAV, MAWA_QUALITY_LOW will usually suffice, since vocals are relatively easy to compress; and MAWA_QUALITY_MEDIUM is always available should you need to compromise for one reason or another.

Deleting a File - MpqDeleteFile
BOOL WINAPI MpqDeleteFile(HANDLE hMPQ, LPCSTR lpFileName);
 
Parameter What it is
 hMPQ [in] The HANDLE of the MPQ to close, which was acquired earlier with MpqOpenArchiveForUpdate. MpqDelete will fail (or worse) if this is NULL or a HANDLE not obtained with MpqOpenArchiveForUpdate.
 lpFileName [in] A pointer to a NULL-terminated name of the file in the MPQ to delete. MpqDeleteFile will crash if this is NULL.

On rare occasions, it may be necessary to delete an unwanted file from an MPQ. To perform this task, MpqDeleteFile is the function for the job. MpqDeleteFile deletes the file lpFileName from the open archive hMPQ. However, it's not quite that simple. MpqDeleteFile removes the hash and file table entries for lpFileName, making it impossible to access. But, MpqDeleteFile doesn't actually free the space the file was occupying unless the it is at the physical end of the MPQ. That means that often the MPQ won't actually decrease in size; however, the space may be recycled and used by a new file added later with MpqAddFileToArchive or MpqAddWAVToArchive.
Just like almost every other function in Storm and StarEdit, MpqDeleteFile returns TRUE on success and FALSE on failure, and you can call GetLastError to get some information about why it failed (assuming it did). In this case, GetLastError is surprisingly helpful; there is basically only one reason MpqDeleteFile will fail (with the exception of hMPQ being invalid): if the file lpFileName doesn't exist in the MPQ. In that case, GetLastError will return MPQ_ERROR_FILE_NOT_FOUND.

Renaming a File - MpqRenameFile
BOOL WINAPI MpqRenameFile(HANDLE hMPQ, LPCSTR lpOldFileName, LPCSTR lpNewFileName);
Parameter What it is
 hMPQ [in] The HANDLE of the MPQ that holds the file to be renamed, and was acquired earlier with MpqOpenArchiveForUpdate. MpqRenameFile will fail if this is NULL or a HANDLE not obtained with MpqOpenArchiveForUpdate.
 lpOldFileName [in] A pointer to a NULL-terminated string containing the name of the file in the MPQ to be renamed. MpqRenameFile will fail if this is NULL.
 lpNewFileName [in] A pointer to a NULL-terminated string containing the name that the file lpOldFileName will be changed to. MpqRenameFile will fail if this is NULL.

As I got further and further in MPQ2K, it became increasingly apparent that StarEdit was lacking a couple useful features. By that time I had considerable knowledge of the workings of Storm and StarEdit, so I decided to write the functions myself. The second of my home-brewed MPQ functions is this one: MpqRenameFile. MpqRenameFile is extremely straightforward; it renames the specified file lpOldFileName in hMPQ to lpNewFileName.
Equally straightforward is the feedback that MpqRenameFile gives you. Following tradition, MpqRenameFile returns TRUE on success and FALSE of failure, and lets you (usually) get the cause of failure with GetLastError. If the hMPQ HANDLE is NULL or invalid, or lpOldFileName or lpNewFileName is NULL, GetLastError will return ERROR_INVALID_PARAMETER. If the file lpOldFileName doesn't exist in the MPQ hMPQ, GetLastError will return MPQ_ERROR_FILE_NOT_FOUND. If the file lpNewFileName exists already in the MPQ, GetLastError will return MPQ_ERROR_ALREADY_EXISTS.

Compacting an MPQ - MpqCompactArchive
BOOL WINAPI MpqCompactArchive(HANDLE hMPQ, BOOL bFailOnBadFile);
Parameter What it is
 hMPQ [in] The HANDLE of the MPQ to compact, which was acquired earlier with MpqOpenArchiveForUpdate. MpqCompactArchive will fail if this is NULL or a HANDLE not obtained with MpqOpenArchiveForUpdate.
 bFailOnBadFile [in] Explained below. Should usually be FALSE.

Recall, if you will, that I said earlier that MpqDeleteFile doesn't actually delete file from an MPQ (not usually, anyway); it just makes them unusable. Well, MpqAddFileToArchive and MpqAddWAVToArchive do the same thing. When you add a file that already exists and the new file is larger than the old one, the new file will be appended, and the space the original occupied won't be freed. Because of this, whenever you create a large, complex MPQ containing many overwritten/deleted files, the will accumulate a substantial amount of dead weight, and there is no way to get rid of it without rebuilding the whole MPQ from scratch.
The solution to this problem is MpqCompactArchive: my third, final, and most difficult designer MPQ function. MpqCompactArchive functions by marshalling data from the MPQ being compacted to a buffer file, and then back again. That means that there must be enough free space remaining on the disk to hold the buffer file (which will be the size of the used portion of the MPQ) during the compacting process.
Although MpqCompactArchive can compact nearly all of the files in any MPQ, there is a certain type of file that it cannot touch: a file that has the MAFA_ENCRYPT and MAFA_MODCRYPTKEY attributes, but lacks the MAFA_COMPRESS and MAFA_COMPRESS2 attributes (for more information, see chapter 5). Thus far, this nasty combination has never been used on any file in any MPQ ever made. But, should MpqCompactArchve ever encounter one of these files, it will set the error code attainable with GetLastError to MPQ_ERROR_COMPACT_ERROR, delete the offending file and continue with the compacting process.
MpqCompactArchive will return TRUE on success, and FALSE on failure. You can obtain more information about a failure by calling GetLastError. If hMPQ is NULL or an invalid StarEdit HANDLE, GetLastError will return ERROR_INVALID_PARAMETER. If there is not enough free memory for MpqCompactArchive to allocate a 2.5 MB read buffer, GetLastError will return ERROR_OUTOFMEMORY. If there isn't enough free disk space for MpqCompactArchive to allocate the buffer file, GetLastError returns ERROR_DISK_FULL. In certain remote situations, GetLastError may return some other strange error code.

Getting Information About a File - SFileGetFileInfo
DWORD WINAPI SFileGetFileInfo(HANDLE hMPQorFile, DWORD dwInfoType);
 
Parameter What it is
 hMPQorFile [in] The HANDLE of the either the MPQ or file inside an MPQ to obtain info about, which was acquired earlier with either SFileOpenArchive or SFileOpenFileEx. SFileGetFileInfo will fail (or worse) if this is NULL or a HANDLE not obtained with SFileOpenArchive or SFileOpenFileEx.
 dwInfoType [in] The type of info to obtain about the file or MPQ. Must be one of the following values defined in lmpqapi.h:
SFILE_INFO_HASH_TABLE_SIZE SFileGetFileInfo will return the hash table size of hMPQorFile (see chapters 2 and 5 for more info). hMPQorFile must be a HANDLE of an MPQ.
SFILE_INFO_NUM_FILES SFileGetFileInfo will return the number of files inside hMPQorFile. hMPQorFile must be a HANDLE of an MPQ.
SFILE_INFO_SIZE SFileGetFileInfo will return the size of the MPQ hMPQorFile or the uncompressed size of the file hMPQorFile. This is functionally equivalent to the SFileGetFileSize function. hMPQorFile can be either a HANDLE of an MPQ or a HANDLE of a file inside.
SFILE_INFO_COMPRESSED_SIZE SFileGetFileInfo will return the compressed size (or uncompressed size if the file isn't compressed) of the file hMPQorFile in an MPQ. hMPQorFile must be a HANDLE of a file inside an MPQ.
SFILE_INFO_FLAGS SFileGetFileInfo will return the flags (see chapter 5 for the meaning of specific flags) of the file hMPQorFile in an MPQ. hMPQorFile must be a HANDLE of a file inside an MPQ.
SFILE_INFO_POSITION SFileGetFileInfo will return the absolute position of the file pointer for the file hMPQorFile in an MPQ. hMPQorFile must be a HANDLE of a file inside an MPQ.

SFileGetFileInfo is, without a doubt, the oddball function in LMPQAPI. Not only is it the only Storm function available through LMPQAPI and not the Storm Interface Library, and the only function that can accept either an MPQ or a file HANDLE, but it actually doesn't exist in Storm at all! Like with StarEdit, it became apparent over time that Storm had some serious deficiencies of its own: namely that it couldn't provide advanced information, such as an MPQ's hash table size, or a file's compressed size. To remedy this, I wrote SFileGetFileInfo, the first of my home-made trio.
SFileGetFileInfo is designed specifically to provide useful, more advanced information about MPQs and the files inside; everything from the number of files in an MPQ to the compressed size of files, to the position of a file's file pointer. It is simple to use, but quite effective; it simple takes the HANDLE of the open MPQ or file to obtain information about in hMPQorFile, and a code for the type of information required in dwInfoType.
If SFileGetFileInfo succeeds, the return value will be the requested information about the file or MPQ. But, if it fails, it will return 0xffffffff, and set an error code you can retrieve with GetLastError. If it failed because the hMPQorFile HANDLE was invalid, GetLastError will return ERROR_INVALID_PARAMETER. If SFileGetFileInfo failed because dwInfoType was an invalid information code, GetLastError will return ERROR_UNKNOWN_PROPERTY. SFileGetFileInfo may also fail because hMPQorFile was an MPQ HANDLE and dwInfoType specifies information that can only be obtained about a file HANDLE, or vice versa, in which case GetLastError will return ERROR_UNKNOWN_PROPERTY also. Other than a critical internal LMPQAPI error, these are the only reasons SFileGetFileInfo will fail.

Back to the Table of Contents

Web site and content copyright © 2002 Justin Olbrantz(Quantam) unless otherwise noted. All rights reserved.