mirror of
				https://github.com/thunderbrewhq/thunderbrew
				synced 2025-10-31 16:26:03 +03:00 
			
		
		
		
	 f86f6d6d09
			
		
	
	
		f86f6d6d09
		
			
		
	
	
	
	
		
			
			* feat(app): add StormLib * feat(app): add OpenArchives * feat(util): update SFile to work with StormLib * feat(app): update SFile * feat(util): update SFile with logging (Windows only) * feat(ui): implemented termination w/o notice * chore(build): update StormLib * chore(util): replace std::string with SStr* functions * fix(stormlib): dwFlags argument for SFileOpenPatchArchive * chore(ui): add Script_* stubs * chore(util): clean up SFile::OpenEx * chore(build): update StormLib --------- Co-authored-by: Phaneron <superp00t@tutanota.com>
		
			
				
	
	
		
			751 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
			
		
		
	
	
			751 lines
		
	
	
		
			24 KiB
		
	
	
	
		
			C++
		
	
	
	
	
	
| /*****************************************************************************/
 | |
| /* 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  0x8000000    // Maximum accepted listfile size is 128 MB
 | |
| 
 | |
| union TListFileHandle
 | |
| {
 | |
|     TFileStream * pStream;              // Opened local file
 | |
|     HANDLE hFile;                       // Opened MPQ file
 | |
| };
 | |
| 
 | |
| 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
 | |
|     DWORD dwFlags;                      // Flags from TMPQArchive
 | |
| 
 | |
| //  char szWildCard[wildcard_length];   // Followed by the name mask (if any)
 | |
| //  char szListFile[listfile_length];   // Followed by the listfile (if any)
 | |
| };
 | |
| 
 | |
| typedef bool (*LOAD_LISTFILE)(TListFileHandle * pHandle, void * pvBuffer, DWORD cbBuffer, LPDWORD pdwBytesRead);
 | |
| 
 | |
| //-----------------------------------------------------------------------------
 | |
| // Local functions (cache)
 | |
| 
 | |
| // In SFileFindFile.cll
 | |
| bool SFileCheckWildCard(const char * szString, const char * szWildCard);
 | |
| 
 | |
| 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 LoadListFile_Stream(TListFileHandle * pHandle, void * pvBuffer, DWORD cbBuffer, LPDWORD pdwBytesRead)
 | |
| {
 | |
|     ULONGLONG ByteOffset = 0;
 | |
|     bool bResult;
 | |
| 
 | |
|     bResult = FileStream_Read(pHandle->pStream, &ByteOffset, pvBuffer, cbBuffer);
 | |
|     if(bResult)
 | |
|         *pdwBytesRead = cbBuffer;
 | |
|     return bResult;
 | |
| }
 | |
| 
 | |
| static bool LoadListFile_MPQ(TListFileHandle * pHandle, void * pvBuffer, DWORD cbBuffer, LPDWORD pdwBytesRead)
 | |
| {
 | |
|     return SFileReadFile(pHandle->hFile, pvBuffer, cbBuffer, pdwBytesRead, NULL);
 | |
| }
 | |
| 
 | |
| static bool FreeListFileCache(TListFileCache * pCache)
 | |
| {
 | |
|     // Valid parameter check
 | |
|     if(pCache != NULL)
 | |
|         STORM_FREE(pCache);
 | |
|     return true;
 | |
| }
 | |
| 
 | |
| static TListFileCache * CreateListFileCache(
 | |
|     LOAD_LISTFILE PfnLoadFile,
 | |
|     TListFileHandle * pHandle,
 | |
|     const char * szWildCard,
 | |
|     DWORD dwFileSize,
 | |
|     DWORD dwMaxSize,
 | |
|     DWORD dwFlags)
 | |
| {
 | |
|     TListFileCache * pCache = NULL;
 | |
|     size_t cchWildCardAligned = 0;
 | |
|     size_t cchWildCard = 0;
 | |
|     DWORD dwBytesRead = 0;
 | |
| 
 | |
|     // Get the amount of bytes that need to be allocated
 | |
|     if(dwFileSize == 0 || dwFileSize > dwMaxSize)
 | |
|         return NULL;
 | |
| 
 | |
|     // Append buffer for name mask, if any
 | |
|     if(szWildCard != NULL)
 | |
|     {
 | |
|         cchWildCard = strlen(szWildCard) + 1;
 | |
|         cchWildCardAligned = (cchWildCard + 3) & 0xFFFFFFFC;
 | |
|     }
 | |
| 
 | |
|     // Allocate cache for one file block
 | |
|     pCache = (TListFileCache *)STORM_ALLOC(BYTE, sizeof(TListFileCache) + cchWildCardAligned + dwFileSize + 1);
 | |
|     if(pCache != NULL)
 | |
|     {
 | |
|         // Clear the entire structure
 | |
|         memset(pCache, 0, sizeof(TListFileCache) + cchWildCard);
 | |
|         pCache->dwFlags = dwFlags;
 | |
| 
 | |
|         // 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) + cchWildCardAligned;
 | |
| 
 | |
|         // Load the entire listfile to the cache
 | |
|         PfnLoadFile(pHandle, pCache->pBegin, dwFileSize, &dwBytesRead);
 | |
|         if(dwBytesRead != 0)
 | |
|         {
 | |
|             // Allocate pointers
 | |
|             pCache->pPos = pCache->pBegin;
 | |
|             pCache->pEnd = pCache->pBegin + dwBytesRead;
 | |
|         }
 | |
|         else
 | |
|         {
 | |
|             FreeListFileCache(pCache);
 | |
|             pCache = NULL;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     // Return the cache
 | |
|     return pCache;
 | |
| }
 | |
| 
 | |
| static TListFileCache * CreateListFileCache(
 | |
|     HANDLE hMpq,
 | |
|     const TCHAR * szListFile,
 | |
|     const char * szWildCard,
 | |
|     DWORD dwMaxSize,
 | |
|     DWORD dwFlags)
 | |
| {
 | |
|     TListFileCache * pCache = NULL;
 | |
|     TListFileHandle ListHandle = {NULL};
 | |
| 
 | |
|     // Put default value to dwMaxSize
 | |
|     if(dwMaxSize == 0)
 | |
|         dwMaxSize = MAX_LISTFILE_SIZE;
 | |
| 
 | |
|     // Internal listfile: hMPQ must be non NULL and szListFile must be NULL.
 | |
|     // We load the MPQ::(listfile) file
 | |
|     if(hMpq != NULL && szListFile == NULL)
 | |
|     {
 | |
|         DWORD dwFileSize = 0;
 | |
| 
 | |
|         // Open the file from the MPQ
 | |
|         if(SFileOpenFileEx(hMpq, LISTFILE_NAME, 0, &ListHandle.hFile))
 | |
|         {
 | |
|             // Get the file size and create the listfile cache
 | |
|             dwFileSize = SFileGetFileSize(ListHandle.hFile, NULL);
 | |
|             pCache = CreateListFileCache(LoadListFile_MPQ, &ListHandle, szWildCard, dwFileSize, dwMaxSize, dwFlags);
 | |
| 
 | |
|             // Close the MPQ file
 | |
|             SFileCloseFile(ListHandle.hFile);
 | |
|         }
 | |
| 
 | |
|         // Return the loaded cache
 | |
|         return pCache;
 | |
|     }
 | |
| 
 | |
|     // External listfile: hMpq must be NULL and szListFile must be non-NULL.
 | |
|     // We load the file using TFileStream
 | |
|     if(hMpq == NULL && szListFile != NULL)
 | |
|     {
 | |
|         ULONGLONG FileSize = 0;
 | |
| 
 | |
|         // Open the local file
 | |
|         ListHandle.pStream = FileStream_OpenFile(szListFile, STREAM_FLAG_READ_ONLY);
 | |
|         if(ListHandle.pStream != NULL)
 | |
|         {
 | |
|             // Verify the file size
 | |
|             FileStream_GetSize(ListHandle.pStream, &FileSize);
 | |
|             if(0 < FileSize && FileSize < dwMaxSize)
 | |
|             {
 | |
|                 pCache = CreateListFileCache(LoadListFile_Stream, &ListHandle, szWildCard, (DWORD)FileSize, dwMaxSize, dwFlags);
 | |
|             }
 | |
| 
 | |
|             // Close the stream
 | |
|             FileStream_Close(ListHandle.pStream);
 | |
|         }
 | |
| 
 | |
|         // Return the loaded cache
 | |
|         return pCache;
 | |
|     }
 | |
| 
 | |
|     // This combination should never happen
 | |
|     SetLastError(ERROR_INVALID_PARAMETER);
 | |
|     assert(false);
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| #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;
 | |
| 
 | |
|     // Skip newlines. Keep spaces and tabs, as they can be a legal part of the file name
 | |
|     while(pCache->pPos < pCache->pEnd && (pCache->pPos[0] == 0x0A || pCache->pPos[0] == 0x0D))
 | |
|         pCache->pPos++;
 | |
| 
 | |
|     // Set the line begin and end
 | |
|     if(pCache->pPos >= pCache->pEnd)
 | |
|         return NULL;
 | |
|     pbLineBegin = pbLineEnd = pCache->pPos;
 | |
| 
 | |
|     // Find the end of the line
 | |
|     while(pCache->pPos < pCache->pEnd && pCache->pPos[0] != 0x0A && pCache->pPos[0] != 0x0D)
 | |
|         pCache->pPos++;
 | |
| 
 | |
|     // Remember the end of the line
 | |
|     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 STORMLIB_CDECL 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 DWORD SListFileCreateNodeForAllLocales(TMPQArchive * ha, const char * szFileName)
 | |
| {
 | |
|     TFileEntry * pFileEntry;
 | |
|     TMPQHash * pHashEnd;
 | |
|     TMPQHash * pHash;
 | |
|     DWORD dwName1;
 | |
|     DWORD dwName2;
 | |
| 
 | |
|     // 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)
 | |
|     {
 | |
|         // Get the end of the hash table and both names
 | |
|         pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize;
 | |
|         dwName1 = ha->pfnHashString(szFileName, MPQ_HASH_NAME_A);
 | |
|         dwName2 = ha->pfnHashString(szFileName, MPQ_HASH_NAME_B);
 | |
| 
 | |
|         // Some protectors set very high hash table size (0x00400000 items or more)
 | |
|         // in order to make this process very slow. We will ignore items
 | |
|         // in the hash table that would be beyond the end of the file.
 | |
|         // Example MPQ: MPQ_2022_v1_Sniper.scx
 | |
|         if(ha->dwFlags & MPQ_FLAG_HASH_TABLE_CUT)
 | |
|             pHashEnd = ha->pHashTable + (ha->dwRealHashTableSize / sizeof(TMPQHash));
 | |
| 
 | |
|         // Go through the hash table and put the name in each item that has the same name pair
 | |
|         for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++)
 | |
|         {
 | |
|             if(pHash->dwName1 == dwName1 && pHash->dwName2 == dwName2 && MPQ_BLOCK_INDEX(pHash) < ha->dwFileTableSize)
 | |
|             {
 | |
|                 // Allocate file name for the file entry
 | |
|                 AllocateFileName(ha, ha->pFileTable + MPQ_BLOCK_INDEX(pHash), szFileName);
 | |
|             }
 | |
|         }
 | |
| 
 | |
|         // Go while we found something
 | |
|         //pFirstHash = pHash = GetFirstHashEntry(ha, szFileName);
 | |
|         //while(pHash != NULL)
 | |
|         //{
 | |
|         //    // Allocate file name for the file entry
 | |
|         //    AllocateFileName(ha, ha->pFileTable + MPQ_BLOCK_INDEX(pHash), 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
 | |
| DWORD SListFileSaveToMpq(TMPQArchive * ha)
 | |
| {
 | |
|     TMPQFile * hf = NULL;
 | |
|     LPBYTE pbListFile;
 | |
|     DWORD cbListFile = 0;
 | |
|     DWORD dwErrCode = 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_DEFAULT_INTERNAL)
 | |
|                 ha->dwFileFlags1 = GetDefaultSpecialFileFlags(cbListFile, ha->pHeader->wFormatVersion);
 | |
| 
 | |
|             // Create the listfile in the MPQ
 | |
|             dwErrCode = SFileAddFile_Init(ha, LISTFILE_NAME,
 | |
|                                            0,
 | |
|                                            cbListFile,
 | |
|                                            LANG_NEUTRAL,
 | |
|                                            ha->dwFileFlags1 | MPQ_FILE_REPLACEEXISTING,
 | |
|                                           &hf);
 | |
| 
 | |
|             // Write the listfile raw data to it
 | |
|             if(dwErrCode == ERROR_SUCCESS)
 | |
|             {
 | |
|                 // Write the content of the listfile to the MPQ
 | |
|                 dwErrCode = 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
 | |
|             dwErrCode = (cbListFile == 0) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return dwErrCode;
 | |
| }
 | |
| 
 | |
| static DWORD SFileAddArbitraryListFile(
 | |
|     TMPQArchive * ha,
 | |
|     HANDLE hMpq,
 | |
|     const TCHAR * szListFile,
 | |
|     DWORD dwMaxSize)
 | |
| {
 | |
|     TListFileCache * pCache = NULL;
 | |
| 
 | |
|     // Create the listfile cache for that file
 | |
|     pCache = CreateListFileCache(hMpq, szListFile, NULL, dwMaxSize, ha->dwFlags);
 | |
|     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 DWORD SFileAddArbitraryListFile(
 | |
|     TMPQArchive * ha,
 | |
|     const char ** listFileEntries,
 | |
|     DWORD dwEntryCount)
 | |
| {
 | |
|     if(listFileEntries != NULL && dwEntryCount > 0)
 | |
|     {
 | |
|         // Get the next line
 | |
|         for(DWORD dwListFileNum = 0; dwListFileNum < dwEntryCount; dwListFileNum++)
 | |
|         {
 | |
|             const char * listFileEntry = listFileEntries[dwListFileNum];
 | |
|             if(listFileEntry != NULL)
 | |
|             {
 | |
|                 SListFileCreateNodeForAllLocales(ha, listFileEntry);
 | |
|             }
 | |
|         }
 | |
|     }
 | |
| 
 | |
|     return (listFileEntries != NULL && dwEntryCount > 0) ? ERROR_SUCCESS : ERROR_INVALID_PARAMETER;
 | |
| }
 | |
| 
 | |
| static DWORD SFileAddInternalListFile(
 | |
|     TMPQArchive * ha,
 | |
|     HANDLE hMpq)
 | |
| {
 | |
|     TMPQHash * pFirstHash;
 | |
|     TMPQHash * pHash;
 | |
|     LCID lcSaveLocale = g_lcFileLocale;
 | |
|     DWORD dwMaxSize = MAX_LISTFILE_SIZE;
 | |
|     DWORD dwErrCode = ERROR_SUCCESS;
 | |
| 
 | |
|     // If there is hash table, we need to support multiple listfiles
 | |
|     // with different locales (BrooDat.mpq)
 | |
|     if(ha->pHashTable != NULL)
 | |
|     {
 | |
|         // If the archive is a malformed map, ignore too large listfiles
 | |
|         if(STORMLIB_TEST_FLAGS(ha->dwFlags, MPQ_FLAG_MALFORMED | MPQ_FLAG_PATCH, MPQ_FLAG_MALFORMED))
 | |
|             dwMaxSize = 0x40000;
 | |
| 
 | |
|         pFirstHash = pHash = GetFirstHashEntry(ha, LISTFILE_NAME);
 | |
|         while(dwErrCode == ERROR_SUCCESS && pHash != NULL)
 | |
|         {
 | |
|             // Set the prefered locale to that from list file
 | |
|             SFileSetLocale(SFILE_MAKE_LCID(pHash->Locale, pHash->Platform));
 | |
| 
 | |
|             // Add that listfile
 | |
|             dwErrCode = SFileAddArbitraryListFile(ha, hMpq, NULL, dwMaxSize);
 | |
| 
 | |
|             // Move to the next hash
 | |
|             pHash = GetNextHashEntry(ha, pFirstHash, pHash);
 | |
|         }
 | |
| 
 | |
|         // Restore the original locale
 | |
|         SFileSetLocale(lcSaveLocale);
 | |
|     }
 | |
|     else
 | |
|     {
 | |
|         // Add the single listfile
 | |
|         dwErrCode = SFileAddArbitraryListFile(ha, hMpq, NULL, dwMaxSize);
 | |
|     }
 | |
| 
 | |
|     // Return the result of the operation
 | |
|     return dwErrCode;
 | |
| }
 | |
| 
 | |
| 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 && SFileCheckWildCard(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.
 | |
| DWORD WINAPI SFileAddListFile(HANDLE hMpq, const TCHAR * szListFile)
 | |
| {
 | |
|     TMPQArchive * ha = (TMPQArchive *)hMpq;
 | |
|     DWORD dwErrCode = ERROR_SUCCESS;
 | |
| 
 | |
|     // Add the listfile for each MPQ in the patch chain
 | |
|     while(ha != NULL)
 | |
|     {
 | |
|         if(szListFile != NULL)
 | |
|             dwErrCode = SFileAddArbitraryListFile(ha, NULL, szListFile, MAX_LISTFILE_SIZE);
 | |
|         else
 | |
|             dwErrCode = 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 dwErrCode;
 | |
| }
 | |
| 
 | |
| DWORD WINAPI SFileAddListFileEntries(HANDLE hMpq, const char ** listFileEntries, DWORD dwEntryCount)
 | |
| {
 | |
|     TMPQArchive * ha = (TMPQArchive *)hMpq;
 | |
|     DWORD dwErrCode = ERROR_SUCCESS;
 | |
| 
 | |
|     // Add the listfile for each MPQ in the patch chain
 | |
|     while(ha != NULL)
 | |
|     {
 | |
|         if(listFileEntries != NULL && dwEntryCount > 0)
 | |
|             dwErrCode = SFileAddArbitraryListFile(ha, listFileEntries, dwEntryCount);
 | |
|         else
 | |
|             dwErrCode = 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 dwErrCode;
 | |
| }
 | |
| 
 | |
| //-----------------------------------------------------------------------------
 | |
| // Enumerating files in listfile
 | |
| 
 | |
| HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const TCHAR * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData)
 | |
| {
 | |
|     TListFileCache * pCache = NULL;
 | |
| 
 | |
|     // Initialize the structure with zeros
 | |
|     memset(lpFindFileData, 0, sizeof(SFILE_FIND_DATA));
 | |
| 
 | |
|     // Open the local/internal listfile
 | |
|     pCache = CreateListFileCache(hMpq, szListFile, szMask, 0, 0);
 | |
|     if(pCache != NULL)
 | |
|     {
 | |
|         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);
 | |
| }
 | |
| 
 |