aboutsummaryrefslogtreecommitdiff
path: root/src/SFileListFile.cpp
diff options
context:
space:
mode:
authorunknown <E:\Ladik\Mail>2015-05-28 17:24:56 +0200
committerunknown <E:\Ladik\Mail>2015-05-28 17:24:56 +0200
commita8a92ccc92f786f56c0c306152de2b7bde53cc45 (patch)
treefe4852eef33c35fc7e783308d301eda429643f60 /src/SFileListFile.cpp
parent61f552875dbcc0879ad69409fff25632460fe087 (diff)
+ Fixed bug in SFileReadFile
Diffstat (limited to 'src/SFileListFile.cpp')
-rw-r--r--src/SFileListFile.cpp1284
1 files changed, 642 insertions, 642 deletions
diff --git a/src/SFileListFile.cpp b/src/SFileListFile.cpp
index e47a9ea..62b8f11 100644
--- a/src/SFileListFile.cpp
+++ b/src/SFileListFile.cpp
@@ -1,642 +1,642 @@
-/*****************************************************************************/
-/* SListFile.cpp Copyright (c) Ladislav Zezula 2004 */
-/*---------------------------------------------------------------------------*/
-/* Description: */
-/*---------------------------------------------------------------------------*/
-/* Date Ver Who Comment */
-/* -------- ---- --- ------- */
-/* 12.06.04 1.00 Lad The first version of SListFile.cpp */
-/*****************************************************************************/
-
-#define __STORMLIB_SELF__
-#include "StormLib.h"
-#include "StormCommon.h"
-#include <assert.h>
-
-//-----------------------------------------------------------------------------
-// Listfile entry structure
-
-#define CACHE_BUFFER_SIZE 0x1000 // Size of the cache buffer
-#define MAX_LISTFILE_SIZE 0x04000000 // Maximum accepted listfile size is about 68 MB
-
-struct TListFileCache
-{
- char * szWildCard; // Self-relative pointer to file mask
- LPBYTE pBegin; // The begin of the listfile cache
- LPBYTE pPos; // Current position in the cache
- LPBYTE pEnd; // The last character in the file cache
-
-// char szWildCard[wildcard_length]; // Followed by the name mask (if any)
-// char szListFile[listfile_length]; // Followed by the listfile (if any)
-};
-
-//-----------------------------------------------------------------------------
-// Local functions (cache)
-
-static char * CopyListLine(char * szListLine, const char * szFileName)
-{
- // Copy the string
- while(szFileName[0] != 0)
- *szListLine++ = *szFileName++;
-
- // Append the end-of-line
- *szListLine++ = 0x0D;
- *szListLine++ = 0x0A;
- return szListLine;
-}
-
-static bool FreeListFileCache(TListFileCache * pCache)
-{
- // Valid parameter check
- if(pCache != NULL)
- STORM_FREE(pCache);
- return true;
-}
-
-static TListFileCache * CreateListFileCache(HANDLE hListFile, const char * szWildCard)
-{
- TListFileCache * pCache = NULL;
- size_t cchWildCard = 0;
- DWORD dwBytesRead = 0;
- DWORD dwFileSize;
-
- // Get the amount of bytes that need to be allocated
- dwFileSize = SFileGetFileSize(hListFile, NULL);
- if(dwFileSize == 0 || dwFileSize > MAX_LISTFILE_SIZE)
- return NULL;
-
- // Append buffer for name mask, if any
- if(szWildCard != NULL)
- cchWildCard = strlen(szWildCard) + 1;
-
- // Allocate cache for one file block
- pCache = (TListFileCache *)STORM_ALLOC(BYTE, sizeof(TListFileCache) + cchWildCard + dwFileSize + 1);
- if(pCache != NULL)
- {
- // Clear the entire structure
- memset(pCache, 0, sizeof(TListFileCache) + cchWildCard);
-
- // Shall we copy the mask?
- if(cchWildCard != 0)
- {
- pCache->szWildCard = (char *)(pCache + 1);
- memcpy(pCache->szWildCard, szWildCard, cchWildCard);
- }
-
- // Fill-in the rest of the cache pointers
- pCache->pBegin = (LPBYTE)(pCache + 1) + cchWildCard;
-
- // Load the entire listfile to the cache
- SFileReadFile(hListFile, pCache->pBegin, dwFileSize, &dwBytesRead, NULL);
- if(dwBytesRead != 0)
- {
- // Allocate pointers
- pCache->pPos = pCache->pBegin;
- pCache->pEnd = pCache->pBegin + dwBytesRead;
- }
- else
- {
- FreeListFileCache(pCache);
- pCache = NULL;
- }
- }
-
- // Return the cache
- return pCache;
-}
-
-#ifdef _DEBUG
-/*
-TMPQNameCache * CreateNameCache(HANDLE hListFile, const char * szSearchMask)
-{
- TMPQNameCache * pNameCache;
- char * szCachePointer;
- size_t cbToAllocate;
- size_t nMaskLength = 1;
- DWORD dwBytesRead = 0;
- DWORD dwFileSize;
-
- // Get the size of the listfile. Ignore zero or too long ones
- dwFileSize = SFileGetFileSize(hListFile, NULL);
- if(dwFileSize == 0 || dwFileSize > MAX_LISTFILE_SIZE)
- return NULL;
-
- // Get the length of the search mask
- if(szSearchMask == NULL)
- szSearchMask = "*";
- nMaskLength = strlen(szSearchMask) + 1;
-
- // Allocate the name cache
- cbToAllocate = sizeof(TMPQNameCache) + nMaskLength + dwFileSize + 1;
- pNameCache = (TMPQNameCache *)STORM_ALLOC(BYTE, cbToAllocate);
- if(pNameCache != NULL)
- {
- // Initialize the name cache
- memset(pNameCache, 0, sizeof(TMPQNameCache));
- pNameCache->TotalCacheSize = (DWORD)(nMaskLength + dwFileSize + 1);
- szCachePointer = (char *)(pNameCache + 1);
-
- // Copy the search mask, if any
- memcpy(szCachePointer, szSearchMask, nMaskLength);
- pNameCache->FirstNameOffset = (DWORD)nMaskLength;
- pNameCache->FreeSpaceOffset = (DWORD)nMaskLength;
-
- // Read the listfile itself
- SFileSetFilePointer(hListFile, 0, NULL, FILE_BEGIN);
- SFileReadFile(hListFile, szCachePointer + nMaskLength, dwFileSize, &dwBytesRead, NULL);
-
- // If nothing has been read from the listfile, clear the cache
- if(dwBytesRead == 0)
- {
- STORM_FREE(pNameCache);
- return NULL;
- }
-
- // Move the free space offset
- pNameCache->FreeSpaceOffset = pNameCache->FirstNameOffset + dwBytesRead + 1;
- szCachePointer[nMaskLength + dwBytesRead] = 0;
- }
-
- return pNameCache;
-}
-
-static void FreeNameCache(TMPQNameCache * pNameCache)
-{
- if(pNameCache != NULL)
- STORM_FREE(pNameCache);
- pNameCache = NULL;
-}
-*/
-#endif // _DEBUG
-
-static char * ReadListFileLine(TListFileCache * pCache, size_t * PtrLength)
-{
- LPBYTE pbLineBegin;
- LPBYTE pbLineEnd;
- LPBYTE pbExtraString = NULL;
-
- // Skip newlines, spaces, tabs and another non-printable stuff
- while(pCache->pPos < pCache->pEnd && pCache->pPos[0] <= 0x20)
- pCache->pPos++;
-
- // Set the line begin and end
- if(pCache->pPos >= pCache->pEnd)
- return NULL;
- pbLineBegin = pbLineEnd = pCache->pPos;
-
- // Copy the remaining characters
- while(pCache->pPos < pCache->pEnd && pCache->pPos[0] != 0x0A && pCache->pPos[0] != 0x0D)
- {
- // Blizzard listfiles can also contain information about patch:
- // Pass1\Files\MacOS\unconditional\user\Background Downloader.app\Contents\Info.plist~Patch(Data#frFR#base-frFR,1326)
- if(pCache->pPos[0] == '~')
- pbExtraString = pCache->pPos;
-
- // Copy the character
- pCache->pPos++;
- }
-
- // If there was extra string after the file name, clear it
- if(pbExtraString != NULL)
- {
- if(pbExtraString[0] == '~' && pbExtraString[1] == 'P')
- {
- pbLineEnd = pbExtraString;
- pbLineEnd[0] = 0;
- }
- }
- else
- {
- pbLineEnd = pCache->pPos++;
- pbLineEnd[0] = 0;
- }
-
- // Give the line to the caller
- if(PtrLength != NULL)
- PtrLength[0] = (size_t)(pbLineEnd - pbLineBegin);
- return (char *)pbLineBegin;
-}
-
-static int CompareFileNodes(const void * p1, const void * p2)
-{
- char * szFileName1 = *(char **)p1;
- char * szFileName2 = *(char **)p2;
-
- return _stricmp(szFileName1, szFileName2);
-}
-
-static LPBYTE CreateListFile(TMPQArchive * ha, DWORD * pcbListFile)
-{
- TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
- TFileEntry * pFileEntry;
- char ** SortTable = NULL;
- char * szListFile = NULL;
- char * szListLine;
- size_t nFileNodes = 0;
- size_t cbListFile = 0;
- size_t nIndex0;
- size_t nIndex1;
-
- // Allocate the table for sorting listfile
- SortTable = STORM_ALLOC(char*, ha->dwFileTableSize);
- if(SortTable == NULL)
- return NULL;
-
- // Construct the sort table
- // Note: in MPQs with multiple locale versions of the same file,
- // this code causes adding multiple listfile entries.
- // They will get removed after the listfile sorting
- for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
- {
- // Only take existing items
- if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && pFileEntry->szFileName != NULL)
- {
- // Ignore pseudo-names and internal names
- if(!IsPseudoFileName(pFileEntry->szFileName, NULL) && !IsInternalMpqFileName(pFileEntry->szFileName))
- {
- SortTable[nFileNodes++] = pFileEntry->szFileName;
- }
- }
- }
-
- // Remove duplicities
- if(nFileNodes > 0)
- {
- // Sort the table
- qsort(SortTable, nFileNodes, sizeof(char *), CompareFileNodes);
-
- // Count the 0-th item
- cbListFile += strlen(SortTable[0]) + 2;
-
- // Walk through the items and only use the ones that are not duplicated
- for(nIndex0 = 0, nIndex1 = 1; nIndex1 < nFileNodes; nIndex1++)
- {
- // If the next file node is different, we will include it to the result listfile
- if(_stricmp(SortTable[nIndex1], SortTable[nIndex0]) != 0)
- {
- cbListFile += strlen(SortTable[nIndex1]) + 2;
- nIndex0 = nIndex1;
- }
- }
-
- // Now allocate buffer for the entire listfile
- szListFile = szListLine = STORM_ALLOC(char, cbListFile + 1);
- if(szListFile != NULL)
- {
- // Copy the 0-th item
- szListLine = CopyListLine(szListLine, SortTable[0]);
-
- // Walk through the items and only use the ones that are not duplicated
- for(nIndex0 = 0, nIndex1 = 1; nIndex1 < nFileNodes; nIndex1++)
- {
- // If the next file node is different, we will include it to the result listfile
- if(_stricmp(SortTable[nIndex1], SortTable[nIndex0]) != 0)
- {
- // Copy the listfile line
- szListLine = CopyListLine(szListLine, SortTable[nIndex1]);
- nIndex0 = nIndex1;
- }
- }
-
- // Sanity check - does the size match?
- assert((size_t)(szListLine - szListFile) == cbListFile);
- }
- }
- else
- {
- szListFile = STORM_ALLOC(char, 1);
- cbListFile = 0;
- }
-
- // Free the sort table
- STORM_FREE(SortTable);
-
- // Give away the listfile
- if(pcbListFile != NULL)
- *pcbListFile = (DWORD)cbListFile;
- return (LPBYTE)szListFile;
-}
-
-//-----------------------------------------------------------------------------
-// Local functions (listfile nodes)
-
-// Adds a name into the list of all names. For each locale in the MPQ,
-// one entry will be created
-// If the file name is already there, does nothing.
-static int SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFileName)
-{
- TFileEntry * pFileEntry;
- TMPQHash * pFirstHash;
- TMPQHash * pHash;
-
- // If we have HET table, use that one
- if(ha->pHetTable != NULL)
- {
- pFileEntry = GetFileEntryLocale(ha, szFileName, 0);
- if(pFileEntry != NULL)
- {
- // Allocate file name for the file entry
- AllocateFileName(ha, pFileEntry, szFileName);
- }
-
- return ERROR_SUCCESS;
- }
-
- // If we have hash table, we use it
- if(ha->pHashTable != NULL)
- {
- // Go while we found something
- pFirstHash = pHash = GetFirstHashEntry(ha, szFileName);
- while(pHash != NULL)
- {
- // Allocate file name for the file entry
- AllocateFileName(ha, ha->pFileTable + pHash->dwBlockIndex, szFileName);
-
- // Now find the next language version of the file
- pHash = GetNextHashEntry(ha, pFirstHash, pHash);
- }
-
- return ERROR_SUCCESS;
- }
-
- return ERROR_CAN_NOT_COMPLETE;
-}
-
-// Saves the whole listfile to the MPQ
-int SListFileSaveToMpq(TMPQArchive * ha)
-{
- TMPQFile * hf = NULL;
- LPBYTE pbListFile;
- DWORD cbListFile = 0;
- int nError = ERROR_SUCCESS;
-
- // Only save the listfile if we should do so
- if(ha->dwFileFlags1 != 0)
- {
- // At this point, we expect to have at least one reserved entry in the file table
- assert(ha->dwFlags & MPQ_FLAG_LISTFILE_NEW);
- assert(ha->dwReservedFiles > 0);
-
- // Create the raw data that is to be written to (listfile)
- // Note: Creating the raw data before the (listfile) has been created in the MPQ
- // causes that the name of the listfile will not be included in the listfile itself.
- // That is OK, because (listfile) in Blizzard MPQs does not contain it either.
- pbListFile = CreateListFile(ha, &cbListFile);
- if(pbListFile != NULL)
- {
- // Determine the real flags for (listfile)
- if(ha->dwFileFlags1 == MPQ_FILE_EXISTS)
- ha->dwFileFlags1 = GetDefaultSpecialFileFlags(cbListFile, ha->pHeader->wFormatVersion);
-
- // Create the listfile in the MPQ
- nError = SFileAddFile_Init(ha, LISTFILE_NAME,
- 0,
- cbListFile,
- LANG_NEUTRAL,
- ha->dwFileFlags1 | MPQ_FILE_REPLACEEXISTING,
- &hf);
-
- // Write the listfile raw data to it
- if(nError == ERROR_SUCCESS)
- {
- // Write the content of the listfile to the MPQ
- nError = SFileAddFile_Write(hf, pbListFile, cbListFile, MPQ_COMPRESSION_ZLIB);
- SFileAddFile_Finish(hf);
- }
-
- // Clear the listfile flags
- ha->dwFlags &= ~(MPQ_FLAG_LISTFILE_NEW | MPQ_FLAG_LISTFILE_NONE);
- ha->dwReservedFiles--;
-
- // Free the listfile buffer
- STORM_FREE(pbListFile);
- }
- else
- {
- // If the (listfile) file would be empty, its OK
- nError = (cbListFile == 0) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY;
- }
- }
-
- return nError;
-}
-
-static int SFileAddArbitraryListFile(
- TMPQArchive * ha,
- HANDLE hListFile)
-{
- TListFileCache * pCache = NULL;
-
- // Create the listfile cache for that file
- pCache = CreateListFileCache(hListFile, NULL);
- if(pCache != NULL)
- {
- char * szFileName;
- size_t nLength = 0;
-
- // Get the next line
- while((szFileName = ReadListFileLine(pCache, &nLength)) != NULL)
- {
- // Add the line to the MPQ
- if(nLength != 0)
- SListFileCreateNodeForAllLocales(ha, szFileName);
- }
-
- // Delete the cache
- FreeListFileCache(pCache);
- }
-
- return (pCache != NULL) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT;
-}
-
-static int SFileAddExternalListFile(
- TMPQArchive * ha,
- HANDLE hMpq,
- const char * szListFile)
-{
- HANDLE hListFile;
- int nError = ERROR_SUCCESS;
-
- // Open the external list file
- if(!SFileOpenFileEx(hMpq, szListFile, SFILE_OPEN_LOCAL_FILE, &hListFile))
- return GetLastError();
-
- // Add the data from the listfile to MPQ
- nError = SFileAddArbitraryListFile(ha, hListFile);
- SFileCloseFile(hListFile);
- return nError;
-}
-
-static int SFileAddInternalListFile(
- TMPQArchive * ha,
- HANDLE hMpq)
-{
- TMPQHash * pFirstHash;
- TMPQHash * pHash;
- HANDLE hListFile;
- DWORD dwFileSize;
- LCID lcSaveLocale = lcFileLocale;
- bool bIgnoreListFile = false;
- int nError = ERROR_SUCCESS;
-
- // If there is hash table, we need to support multiple listfiles
- // with different locales (BrooDat.mpq)
- if(ha->pHashTable != NULL)
- {
- pFirstHash = pHash = GetFirstHashEntry(ha, LISTFILE_NAME);
- while(nError == ERROR_SUCCESS && pHash != NULL)
- {
- // Set the prefered locale to that from list file
- SFileSetLocale(pHash->lcLocale);
-
- // Attempt to open the file with that locale
- if(SFileOpenFileEx(hMpq, LISTFILE_NAME, 0, &hListFile))
- {
- // If the archive is a malformed map, ignore too large listfiles
- if(ha->dwFlags & MPQ_FLAG_MALFORMED)
- {
- dwFileSize = SFileGetFileSize(hListFile, NULL);
- bIgnoreListFile = (dwFileSize > 0x40000);
- }
-
- // Add the data from the listfile to MPQ
- if(bIgnoreListFile == false)
- nError = SFileAddArbitraryListFile(ha, hListFile);
- SFileCloseFile(hListFile);
- }
-
- // Restore the original locale
- SFileSetLocale(lcSaveLocale);
-
- // Move to the next hash
- pHash = GetNextHashEntry(ha, pFirstHash, pHash);
- }
- }
- else
- {
- // Open the external list file
- if(SFileOpenFileEx(hMpq, LISTFILE_NAME, 0, &hListFile))
- {
- // Add the data from the listfile to MPQ
- // The function also closes the listfile handle
- nError = SFileAddArbitraryListFile(ha, hListFile);
- SFileCloseFile(hListFile);
- }
- }
-
- // Return the result of the operation
- return nError;
-}
-
-static bool DoListFileSearch(TListFileCache * pCache, SFILE_FIND_DATA * lpFindFileData)
-{
- // Check for the valid search handle
- if(pCache != NULL)
- {
- char * szFileName;
- size_t nLength = 0;
-
- // Get the next line
- while((szFileName = ReadListFileLine(pCache, &nLength)) != NULL)
- {
- // Check search mask
- if(nLength != 0 && CheckWildCard(szFileName, pCache->szWildCard))
- {
- if(nLength >= sizeof(lpFindFileData->cFileName))
- nLength = sizeof(lpFindFileData->cFileName) - 1;
-
- memcpy(lpFindFileData->cFileName, szFileName, nLength);
- lpFindFileData->cFileName[nLength] = 0;
- return true;
- }
- }
- }
-
- // No more files
- memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
- SetLastError(ERROR_NO_MORE_FILES);
- return false;
-}
-
-//-----------------------------------------------------------------------------
-// File functions
-
-// Adds a listfile into the MPQ archive.
-int WINAPI SFileAddListFile(HANDLE hMpq, const char * szListFile)
-{
- TMPQArchive * ha = (TMPQArchive *)hMpq;
- int nError = ERROR_SUCCESS;
-
- // Add the listfile for each MPQ in the patch chain
- while(ha != NULL)
- {
- if(szListFile != NULL)
- nError = SFileAddExternalListFile(ha, hMpq, szListFile);
- else
- nError = SFileAddInternalListFile(ha, hMpq);
-
- // Also, add three special files to the listfile:
- // (listfile) itself, (attributes) and (signature)
- SListFileCreateNodeForAllLocales(ha, LISTFILE_NAME);
- SListFileCreateNodeForAllLocales(ha, SIGNATURE_NAME);
- SListFileCreateNodeForAllLocales(ha, ATTRIBUTES_NAME);
-
- // Move to the next archive in the chain
- ha = ha->haPatch;
- }
-
- return nError;
-}
-
-//-----------------------------------------------------------------------------
-// Enumerating files in listfile
-
-HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData)
-{
- TListFileCache * pCache = NULL;
- HANDLE hListFile = NULL;
- DWORD dwSearchScope = SFILE_OPEN_LOCAL_FILE;
-
- // Initialize the structure with zeros
- memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
-
- // If the szListFile is NULL, it means we have to open internal listfile
- if(szListFile == NULL)
- {
- // Use SFILE_OPEN_ANY_LOCALE for listfile. This will allow us to load
- // the listfile even if there is only non-neutral version of the listfile in the MPQ
- dwSearchScope = SFILE_OPEN_ANY_LOCALE;
- szListFile = LISTFILE_NAME;
- }
-
- // Open the local/internal listfile
- if(SFileOpenFileEx(hMpq, szListFile, dwSearchScope, &hListFile))
- {
- pCache = CreateListFileCache(hListFile, szMask);
- SFileCloseFile(hListFile);
- }
-
- if(!DoListFileSearch(pCache, lpFindFileData))
- {
- memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
- SetLastError(ERROR_NO_MORE_FILES);
- FreeListFileCache(pCache);
- pCache = NULL;
- }
-
- // Return the listfile cache as handle
- return (HANDLE)pCache;
-}
-
-bool WINAPI SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData)
-{
- return DoListFileSearch((TListFileCache *)hFind, lpFindFileData);
-}
-
-bool WINAPI SListFileFindClose(HANDLE hFind)
-{
- TListFileCache * pCache = (TListFileCache *)hFind;
-
- return FreeListFileCache(pCache);
-}
-
+/*****************************************************************************/
+/* SListFile.cpp Copyright (c) Ladislav Zezula 2004 */
+/*---------------------------------------------------------------------------*/
+/* Description: */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* 12.06.04 1.00 Lad The first version of SListFile.cpp */
+/*****************************************************************************/
+
+#define __STORMLIB_SELF__
+#include "StormLib.h"
+#include "StormCommon.h"
+#include <assert.h>
+
+//-----------------------------------------------------------------------------
+// Listfile entry structure
+
+#define CACHE_BUFFER_SIZE 0x1000 // Size of the cache buffer
+#define MAX_LISTFILE_SIZE 0x04000000 // Maximum accepted listfile size is about 68 MB
+
+struct TListFileCache
+{
+ char * szWildCard; // Self-relative pointer to file mask
+ LPBYTE pBegin; // The begin of the listfile cache
+ LPBYTE pPos; // Current position in the cache
+ LPBYTE pEnd; // The last character in the file cache
+
+// char szWildCard[wildcard_length]; // Followed by the name mask (if any)
+// char szListFile[listfile_length]; // Followed by the listfile (if any)
+};
+
+//-----------------------------------------------------------------------------
+// Local functions (cache)
+
+static char * CopyListLine(char * szListLine, const char * szFileName)
+{
+ // Copy the string
+ while(szFileName[0] != 0)
+ *szListLine++ = *szFileName++;
+
+ // Append the end-of-line
+ *szListLine++ = 0x0D;
+ *szListLine++ = 0x0A;
+ return szListLine;
+}
+
+static bool FreeListFileCache(TListFileCache * pCache)
+{
+ // Valid parameter check
+ if(pCache != NULL)
+ STORM_FREE(pCache);
+ return true;
+}
+
+static TListFileCache * CreateListFileCache(HANDLE hListFile, const char * szWildCard)
+{
+ TListFileCache * pCache = NULL;
+ size_t cchWildCard = 0;
+ DWORD dwBytesRead = 0;
+ DWORD dwFileSize;
+
+ // Get the amount of bytes that need to be allocated
+ dwFileSize = SFileGetFileSize(hListFile, NULL);
+ if(dwFileSize == 0 || dwFileSize > MAX_LISTFILE_SIZE)
+ return NULL;
+
+ // Append buffer for name mask, if any
+ if(szWildCard != NULL)
+ cchWildCard = strlen(szWildCard) + 1;
+
+ // Allocate cache for one file block
+ pCache = (TListFileCache *)STORM_ALLOC(BYTE, sizeof(TListFileCache) + cchWildCard + dwFileSize + 1);
+ if(pCache != NULL)
+ {
+ // Clear the entire structure
+ memset(pCache, 0, sizeof(TListFileCache) + cchWildCard);
+
+ // Shall we copy the mask?
+ if(cchWildCard != 0)
+ {
+ pCache->szWildCard = (char *)(pCache + 1);
+ memcpy(pCache->szWildCard, szWildCard, cchWildCard);
+ }
+
+ // Fill-in the rest of the cache pointers
+ pCache->pBegin = (LPBYTE)(pCache + 1) + cchWildCard;
+
+ // Load the entire listfile to the cache
+ SFileReadFile(hListFile, pCache->pBegin, dwFileSize, &dwBytesRead, NULL);
+ if(dwBytesRead != 0)
+ {
+ // Allocate pointers
+ pCache->pPos = pCache->pBegin;
+ pCache->pEnd = pCache->pBegin + dwBytesRead;
+ }
+ else
+ {
+ FreeListFileCache(pCache);
+ pCache = NULL;
+ }
+ }
+
+ // Return the cache
+ return pCache;
+}
+
+#ifdef _DEBUG
+/*
+TMPQNameCache * CreateNameCache(HANDLE hListFile, const char * szSearchMask)
+{
+ TMPQNameCache * pNameCache;
+ char * szCachePointer;
+ size_t cbToAllocate;
+ size_t nMaskLength = 1;
+ DWORD dwBytesRead = 0;
+ DWORD dwFileSize;
+
+ // Get the size of the listfile. Ignore zero or too long ones
+ dwFileSize = SFileGetFileSize(hListFile, NULL);
+ if(dwFileSize == 0 || dwFileSize > MAX_LISTFILE_SIZE)
+ return NULL;
+
+ // Get the length of the search mask
+ if(szSearchMask == NULL)
+ szSearchMask = "*";
+ nMaskLength = strlen(szSearchMask) + 1;
+
+ // Allocate the name cache
+ cbToAllocate = sizeof(TMPQNameCache) + nMaskLength + dwFileSize + 1;
+ pNameCache = (TMPQNameCache *)STORM_ALLOC(BYTE, cbToAllocate);
+ if(pNameCache != NULL)
+ {
+ // Initialize the name cache
+ memset(pNameCache, 0, sizeof(TMPQNameCache));
+ pNameCache->TotalCacheSize = (DWORD)(nMaskLength + dwFileSize + 1);
+ szCachePointer = (char *)(pNameCache + 1);
+
+ // Copy the search mask, if any
+ memcpy(szCachePointer, szSearchMask, nMaskLength);
+ pNameCache->FirstNameOffset = (DWORD)nMaskLength;
+ pNameCache->FreeSpaceOffset = (DWORD)nMaskLength;
+
+ // Read the listfile itself
+ SFileSetFilePointer(hListFile, 0, NULL, FILE_BEGIN);
+ SFileReadFile(hListFile, szCachePointer + nMaskLength, dwFileSize, &dwBytesRead, NULL);
+
+ // If nothing has been read from the listfile, clear the cache
+ if(dwBytesRead == 0)
+ {
+ STORM_FREE(pNameCache);
+ return NULL;
+ }
+
+ // Move the free space offset
+ pNameCache->FreeSpaceOffset = pNameCache->FirstNameOffset + dwBytesRead + 1;
+ szCachePointer[nMaskLength + dwBytesRead] = 0;
+ }
+
+ return pNameCache;
+}
+
+static void FreeNameCache(TMPQNameCache * pNameCache)
+{
+ if(pNameCache != NULL)
+ STORM_FREE(pNameCache);
+ pNameCache = NULL;
+}
+*/
+#endif // _DEBUG
+
+static char * ReadListFileLine(TListFileCache * pCache, size_t * PtrLength)
+{
+ LPBYTE pbLineBegin;
+ LPBYTE pbLineEnd;
+ LPBYTE pbExtraString = NULL;
+
+ // Skip newlines, spaces, tabs and another non-printable stuff
+ while(pCache->pPos < pCache->pEnd && pCache->pPos[0] <= 0x20)
+ pCache->pPos++;
+
+ // Set the line begin and end
+ if(pCache->pPos >= pCache->pEnd)
+ return NULL;
+ pbLineBegin = pbLineEnd = pCache->pPos;
+
+ // Copy the remaining characters
+ while(pCache->pPos < pCache->pEnd && pCache->pPos[0] != 0x0A && pCache->pPos[0] != 0x0D)
+ {
+ // Blizzard listfiles can also contain information about patch:
+ // Pass1\Files\MacOS\unconditional\user\Background Downloader.app\Contents\Info.plist~Patch(Data#frFR#base-frFR,1326)
+ if(pCache->pPos[0] == '~')
+ pbExtraString = pCache->pPos;
+
+ // Copy the character
+ pCache->pPos++;
+ }
+
+ // If there was extra string after the file name, clear it
+ if(pbExtraString != NULL)
+ {
+ if(pbExtraString[0] == '~' && pbExtraString[1] == 'P')
+ {
+ pbLineEnd = pbExtraString;
+ pbLineEnd[0] = 0;
+ }
+ }
+ else
+ {
+ pbLineEnd = pCache->pPos++;
+ pbLineEnd[0] = 0;
+ }
+
+ // Give the line to the caller
+ if(PtrLength != NULL)
+ PtrLength[0] = (size_t)(pbLineEnd - pbLineBegin);
+ return (char *)pbLineBegin;
+}
+
+static int CompareFileNodes(const void * p1, const void * p2)
+{
+ char * szFileName1 = *(char **)p1;
+ char * szFileName2 = *(char **)p2;
+
+ return _stricmp(szFileName1, szFileName2);
+}
+
+static LPBYTE CreateListFile(TMPQArchive * ha, DWORD * pcbListFile)
+{
+ TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
+ TFileEntry * pFileEntry;
+ char ** SortTable = NULL;
+ char * szListFile = NULL;
+ char * szListLine;
+ size_t nFileNodes = 0;
+ size_t cbListFile = 0;
+ size_t nIndex0;
+ size_t nIndex1;
+
+ // Allocate the table for sorting listfile
+ SortTable = STORM_ALLOC(char*, ha->dwFileTableSize);
+ if(SortTable == NULL)
+ return NULL;
+
+ // Construct the sort table
+ // Note: in MPQs with multiple locale versions of the same file,
+ // this code causes adding multiple listfile entries.
+ // They will get removed after the listfile sorting
+ for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
+ {
+ // Only take existing items
+ if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && pFileEntry->szFileName != NULL)
+ {
+ // Ignore pseudo-names and internal names
+ if(!IsPseudoFileName(pFileEntry->szFileName, NULL) && !IsInternalMpqFileName(pFileEntry->szFileName))
+ {
+ SortTable[nFileNodes++] = pFileEntry->szFileName;
+ }
+ }
+ }
+
+ // Remove duplicities
+ if(nFileNodes > 0)
+ {
+ // Sort the table
+ qsort(SortTable, nFileNodes, sizeof(char *), CompareFileNodes);
+
+ // Count the 0-th item
+ cbListFile += strlen(SortTable[0]) + 2;
+
+ // Walk through the items and only use the ones that are not duplicated
+ for(nIndex0 = 0, nIndex1 = 1; nIndex1 < nFileNodes; nIndex1++)
+ {
+ // If the next file node is different, we will include it to the result listfile
+ if(_stricmp(SortTable[nIndex1], SortTable[nIndex0]) != 0)
+ {
+ cbListFile += strlen(SortTable[nIndex1]) + 2;
+ nIndex0 = nIndex1;
+ }
+ }
+
+ // Now allocate buffer for the entire listfile
+ szListFile = szListLine = STORM_ALLOC(char, cbListFile + 1);
+ if(szListFile != NULL)
+ {
+ // Copy the 0-th item
+ szListLine = CopyListLine(szListLine, SortTable[0]);
+
+ // Walk through the items and only use the ones that are not duplicated
+ for(nIndex0 = 0, nIndex1 = 1; nIndex1 < nFileNodes; nIndex1++)
+ {
+ // If the next file node is different, we will include it to the result listfile
+ if(_stricmp(SortTable[nIndex1], SortTable[nIndex0]) != 0)
+ {
+ // Copy the listfile line
+ szListLine = CopyListLine(szListLine, SortTable[nIndex1]);
+ nIndex0 = nIndex1;
+ }
+ }
+
+ // Sanity check - does the size match?
+ assert((size_t)(szListLine - szListFile) == cbListFile);
+ }
+ }
+ else
+ {
+ szListFile = STORM_ALLOC(char, 1);
+ cbListFile = 0;
+ }
+
+ // Free the sort table
+ STORM_FREE(SortTable);
+
+ // Give away the listfile
+ if(pcbListFile != NULL)
+ *pcbListFile = (DWORD)cbListFile;
+ return (LPBYTE)szListFile;
+}
+
+//-----------------------------------------------------------------------------
+// Local functions (listfile nodes)
+
+// Adds a name into the list of all names. For each locale in the MPQ,
+// one entry will be created
+// If the file name is already there, does nothing.
+static int SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFileName)
+{
+ TFileEntry * pFileEntry;
+ TMPQHash * pFirstHash;
+ TMPQHash * pHash;
+
+ // If we have HET table, use that one
+ if(ha->pHetTable != NULL)
+ {
+ pFileEntry = GetFileEntryLocale(ha, szFileName, 0);
+ if(pFileEntry != NULL)
+ {
+ // Allocate file name for the file entry
+ AllocateFileName(ha, pFileEntry, szFileName);
+ }
+
+ return ERROR_SUCCESS;
+ }
+
+ // If we have hash table, we use it
+ if(ha->pHashTable != NULL)
+ {
+ // Go while we found something
+ pFirstHash = pHash = GetFirstHashEntry(ha, szFileName);
+ while(pHash != NULL)
+ {
+ // Allocate file name for the file entry
+ AllocateFileName(ha, ha->pFileTable + pHash->dwBlockIndex, szFileName);
+
+ // Now find the next language version of the file
+ pHash = GetNextHashEntry(ha, pFirstHash, pHash);
+ }
+
+ return ERROR_SUCCESS;
+ }
+
+ return ERROR_CAN_NOT_COMPLETE;
+}
+
+// Saves the whole listfile to the MPQ
+int SListFileSaveToMpq(TMPQArchive * ha)
+{
+ TMPQFile * hf = NULL;
+ LPBYTE pbListFile;
+ DWORD cbListFile = 0;
+ int nError = ERROR_SUCCESS;
+
+ // Only save the listfile if we should do so
+ if(ha->dwFileFlags1 != 0)
+ {
+ // At this point, we expect to have at least one reserved entry in the file table
+ assert(ha->dwFlags & MPQ_FLAG_LISTFILE_NEW);
+ assert(ha->dwReservedFiles > 0);
+
+ // Create the raw data that is to be written to (listfile)
+ // Note: Creating the raw data before the (listfile) has been created in the MPQ
+ // causes that the name of the listfile will not be included in the listfile itself.
+ // That is OK, because (listfile) in Blizzard MPQs does not contain it either.
+ pbListFile = CreateListFile(ha, &cbListFile);
+ if(pbListFile != NULL)
+ {
+ // Determine the real flags for (listfile)
+ if(ha->dwFileFlags1 == MPQ_FILE_EXISTS)
+ ha->dwFileFlags1 = GetDefaultSpecialFileFlags(cbListFile, ha->pHeader->wFormatVersion);
+
+ // Create the listfile in the MPQ
+ nError = SFileAddFile_Init(ha, LISTFILE_NAME,
+ 0,
+ cbListFile,
+ LANG_NEUTRAL,
+ ha->dwFileFlags1 | MPQ_FILE_REPLACEEXISTING,
+ &hf);
+
+ // Write the listfile raw data to it
+ if(nError == ERROR_SUCCESS)
+ {
+ // Write the content of the listfile to the MPQ
+ nError = SFileAddFile_Write(hf, pbListFile, cbListFile, MPQ_COMPRESSION_ZLIB);
+ SFileAddFile_Finish(hf);
+ }
+
+ // Clear the listfile flags
+ ha->dwFlags &= ~(MPQ_FLAG_LISTFILE_NEW | MPQ_FLAG_LISTFILE_NONE);
+ ha->dwReservedFiles--;
+
+ // Free the listfile buffer
+ STORM_FREE(pbListFile);
+ }
+ else
+ {
+ // If the (listfile) file would be empty, its OK
+ nError = (cbListFile == 0) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ return nError;
+}
+
+static int SFileAddArbitraryListFile(
+ TMPQArchive * ha,
+ HANDLE hListFile)
+{
+ TListFileCache * pCache = NULL;
+
+ // Create the listfile cache for that file
+ pCache = CreateListFileCache(hListFile, NULL);
+ if(pCache != NULL)
+ {
+ char * szFileName;
+ size_t nLength = 0;
+
+ // Get the next line
+ while((szFileName = ReadListFileLine(pCache, &nLength)) != NULL)
+ {
+ // Add the line to the MPQ
+ if(nLength != 0)
+ SListFileCreateNodeForAllLocales(ha, szFileName);
+ }
+
+ // Delete the cache
+ FreeListFileCache(pCache);
+ }
+
+ return (pCache != NULL) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT;
+}
+
+static int SFileAddExternalListFile(
+ TMPQArchive * ha,
+ HANDLE hMpq,
+ const char * szListFile)
+{
+ HANDLE hListFile;
+ int nError = ERROR_SUCCESS;
+
+ // Open the external list file
+ if(!SFileOpenFileEx(hMpq, szListFile, SFILE_OPEN_LOCAL_FILE, &hListFile))
+ return GetLastError();
+
+ // Add the data from the listfile to MPQ
+ nError = SFileAddArbitraryListFile(ha, hListFile);
+ SFileCloseFile(hListFile);
+ return nError;
+}
+
+static int SFileAddInternalListFile(
+ TMPQArchive * ha,
+ HANDLE hMpq)
+{
+ TMPQHash * pFirstHash;
+ TMPQHash * pHash;
+ HANDLE hListFile;
+ DWORD dwFileSize;
+ LCID lcSaveLocale = lcFileLocale;
+ bool bIgnoreListFile = false;
+ int nError = ERROR_SUCCESS;
+
+ // If there is hash table, we need to support multiple listfiles
+ // with different locales (BrooDat.mpq)
+ if(ha->pHashTable != NULL)
+ {
+ pFirstHash = pHash = GetFirstHashEntry(ha, LISTFILE_NAME);
+ while(nError == ERROR_SUCCESS && pHash != NULL)
+ {
+ // Set the prefered locale to that from list file
+ SFileSetLocale(pHash->lcLocale);
+
+ // Attempt to open the file with that locale
+ if(SFileOpenFileEx(hMpq, LISTFILE_NAME, 0, &hListFile))
+ {
+ // If the archive is a malformed map, ignore too large listfiles
+ if(ha->dwFlags & MPQ_FLAG_MALFORMED)
+ {
+ dwFileSize = SFileGetFileSize(hListFile, NULL);
+ bIgnoreListFile = (dwFileSize > 0x40000);
+ }
+
+ // Add the data from the listfile to MPQ
+ if(bIgnoreListFile == false)
+ nError = SFileAddArbitraryListFile(ha, hListFile);
+ SFileCloseFile(hListFile);
+ }
+
+ // Restore the original locale
+ SFileSetLocale(lcSaveLocale);
+
+ // Move to the next hash
+ pHash = GetNextHashEntry(ha, pFirstHash, pHash);
+ }
+ }
+ else
+ {
+ // Open the external list file
+ if(SFileOpenFileEx(hMpq, LISTFILE_NAME, 0, &hListFile))
+ {
+ // Add the data from the listfile to MPQ
+ // The function also closes the listfile handle
+ nError = SFileAddArbitraryListFile(ha, hListFile);
+ SFileCloseFile(hListFile);
+ }
+ }
+
+ // Return the result of the operation
+ return nError;
+}
+
+static bool DoListFileSearch(TListFileCache * pCache, SFILE_FIND_DATA * lpFindFileData)
+{
+ // Check for the valid search handle
+ if(pCache != NULL)
+ {
+ char * szFileName;
+ size_t nLength = 0;
+
+ // Get the next line
+ while((szFileName = ReadListFileLine(pCache, &nLength)) != NULL)
+ {
+ // Check search mask
+ if(nLength != 0 && CheckWildCard(szFileName, pCache->szWildCard))
+ {
+ if(nLength >= sizeof(lpFindFileData->cFileName))
+ nLength = sizeof(lpFindFileData->cFileName) - 1;
+
+ memcpy(lpFindFileData->cFileName, szFileName, nLength);
+ lpFindFileData->cFileName[nLength] = 0;
+ return true;
+ }
+ }
+ }
+
+ // No more files
+ memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
+ SetLastError(ERROR_NO_MORE_FILES);
+ return false;
+}
+
+//-----------------------------------------------------------------------------
+// File functions
+
+// Adds a listfile into the MPQ archive.
+int WINAPI SFileAddListFile(HANDLE hMpq, const char * szListFile)
+{
+ TMPQArchive * ha = (TMPQArchive *)hMpq;
+ int nError = ERROR_SUCCESS;
+
+ // Add the listfile for each MPQ in the patch chain
+ while(ha != NULL)
+ {
+ if(szListFile != NULL)
+ nError = SFileAddExternalListFile(ha, hMpq, szListFile);
+ else
+ nError = SFileAddInternalListFile(ha, hMpq);
+
+ // Also, add three special files to the listfile:
+ // (listfile) itself, (attributes) and (signature)
+ SListFileCreateNodeForAllLocales(ha, LISTFILE_NAME);
+ SListFileCreateNodeForAllLocales(ha, SIGNATURE_NAME);
+ SListFileCreateNodeForAllLocales(ha, ATTRIBUTES_NAME);
+
+ // Move to the next archive in the chain
+ ha = ha->haPatch;
+ }
+
+ return nError;
+}
+
+//-----------------------------------------------------------------------------
+// Enumerating files in listfile
+
+HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const char * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData)
+{
+ TListFileCache * pCache = NULL;
+ HANDLE hListFile = NULL;
+ DWORD dwSearchScope = SFILE_OPEN_LOCAL_FILE;
+
+ // Initialize the structure with zeros
+ memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
+
+ // If the szListFile is NULL, it means we have to open internal listfile
+ if(szListFile == NULL)
+ {
+ // Use SFILE_OPEN_ANY_LOCALE for listfile. This will allow us to load
+ // the listfile even if there is only non-neutral version of the listfile in the MPQ
+ dwSearchScope = SFILE_OPEN_ANY_LOCALE;
+ szListFile = LISTFILE_NAME;
+ }
+
+ // Open the local/internal listfile
+ if(SFileOpenFileEx(hMpq, szListFile, dwSearchScope, &hListFile))
+ {
+ pCache = CreateListFileCache(hListFile, szMask);
+ SFileCloseFile(hListFile);
+ }
+
+ if(!DoListFileSearch(pCache, lpFindFileData))
+ {
+ memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
+ SetLastError(ERROR_NO_MORE_FILES);
+ FreeListFileCache(pCache);
+ pCache = NULL;
+ }
+
+ // Return the listfile cache as handle
+ return (HANDLE)pCache;
+}
+
+bool WINAPI SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData)
+{
+ return DoListFileSearch((TListFileCache *)hFind, lpFindFileData);
+}
+
+bool WINAPI SListFileFindClose(HANDLE hFind)
+{
+ TListFileCache * pCache = (TListFileCache *)hFind;
+
+ return FreeListFileCache(pCache);
+}
+