diff options
| author | KiritoDv <kiritodev01@gmail.com> | 2023-08-17 00:35:58 -0600 |
|---|---|---|
| committer | KiritoDv <kiritodev01@gmail.com> | 2023-08-17 00:35:58 -0600 |
| commit | 6fd8e5ee43dd0f87eb4e57b86e84770fcd95db25 (patch) | |
| tree | 6a22e06c6105b63a0ae560f571f6d4e2522ba364 /lib/StormLib/src/SFileOpenArchive.cpp | |
Added StormLib, nlohmann, BinaryTools and CLI11
Diffstat (limited to 'lib/StormLib/src/SFileOpenArchive.cpp')
| -rw-r--r-- | lib/StormLib/src/SFileOpenArchive.cpp | 692 |
1 files changed, 692 insertions, 0 deletions
diff --git a/lib/StormLib/src/SFileOpenArchive.cpp b/lib/StormLib/src/SFileOpenArchive.cpp new file mode 100644 index 0000000..16e2491 --- /dev/null +++ b/lib/StormLib/src/SFileOpenArchive.cpp @@ -0,0 +1,692 @@ +/*****************************************************************************/ +/* SFileOpenArchive.cpp Copyright Ladislav Zezula 1999 */ +/* */ +/* Author : Ladislav Zezula */ +/* E-mail : ladik@zezula.net */ +/* WWW : www.zezula.net */ +/*---------------------------------------------------------------------------*/ +/* Implementation of archive functions */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* xx.xx.xx 1.00 Lad Created */ +/* 19.11.03 1.01 Dan Big endian handling */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" + +#define HEADER_SEARCH_BUFFER_SIZE 0x1000 + +//----------------------------------------------------------------------------- +// Local functions + +static MTYPE CheckMapType(LPCTSTR szFileName, LPBYTE pbHeaderBuffer, size_t cbHeaderBuffer) +{ + LPDWORD HeaderInt32 = (LPDWORD)pbHeaderBuffer; + LPCTSTR szExtension; + + // Don't do any checks if there is not at least 16 bytes + if(cbHeaderBuffer > 0x10) + { + DWORD DwordValue0 = BSWAP_INT32_UNSIGNED(HeaderInt32[0]); + DWORD DwordValue1 = BSWAP_INT32_UNSIGNED(HeaderInt32[1]); + DWORD DwordValue2 = BSWAP_INT32_UNSIGNED(HeaderInt32[2]); + DWORD DwordValue3 = BSWAP_INT32_UNSIGNED(HeaderInt32[3]); + + // Check maps by extension (Starcraft, Starcraft II). We must do this before + // checking actual data, because the "NP_Protect" protector places + // fake Warcraft III header into the Starcraft II maps + if((szExtension = _tcsrchr(szFileName, _T('.'))) != NULL) + { + // Check for Starcraft II maps by extension + if(!_tcsicmp(szExtension, _T(".s2ma")) || !_tcsicmp(szExtension, _T(".SC2Map")) || !_tcsicmp(szExtension, _T(".SC2Mod"))) + { + return MapTypeStarcraft2; + } + + // Check for Starcraft I maps by extension + if(!_tcsicmp(szExtension, _T(".scm")) || !_tcsicmp(szExtension, _T(".scx"))) + { + return MapTypeStarcraft; + } + } + + // Test for AVI files (Warcraft III cinematics) - 'RIFF', 'AVI ' or 'LIST' + if(DwordValue0 == 0x46464952 && DwordValue2 == 0x20495641 && DwordValue3 == 0x5453494C) + return MapTypeAviFile; + + // Check for Warcraft III maps + if(DwordValue0 == 0x57334D48 && DwordValue1 == 0x00000000) + return MapTypeWarcraft3; + } + + // MIX files are DLL files that contain MPQ in overlay. + // Only Warcraft III is able to load them, so we consider them Warcraft III maps + if(cbHeaderBuffer > 0x200 && pbHeaderBuffer[0] == 'M' && pbHeaderBuffer[1] == 'Z') + { + // Check the value of IMAGE_DOS_HEADER::e_lfanew at offset 0x3C + if(0 < HeaderInt32[0x0F] && HeaderInt32[0x0F] < 0x10000) + return MapTypeWarcraft3; + } + + // No special map type recognized + return MapTypeNotRecognized; +} + +static bool IsStarcraftBetaArchive(TMPQHeader * pHeader) +{ + // The archive must be version 1, with a standard header size + if(pHeader->dwID == ID_MPQ && pHeader->dwHeaderSize == MPQ_HEADER_SIZE_V1) + { + // Check for known archive sizes + return (pHeader->dwArchiveSize == 0x00028FB3 || // patch_rt.mpq + pHeader->dwArchiveSize == 0x0351853D || // StarDat.mpq + pHeader->dwArchiveSize == 0x0AEC8960); // INSTALL.exe + + } + return false; +} + +static TMPQUserData * IsValidMpqUserData(ULONGLONG ByteOffset, ULONGLONG FileSize, void * pvUserData) +{ + TMPQUserData * pUserData; + + // BSWAP the source data and copy them to our buffer + BSWAP_ARRAY32_UNSIGNED(pvUserData, sizeof(TMPQUserData)); + pUserData = (TMPQUserData *)pvUserData; + + // Check the sizes + if(pUserData->cbUserDataHeader <= pUserData->cbUserDataSize && pUserData->cbUserDataSize <= pUserData->dwHeaderOffs) + { + // Move to the position given by the userdata + ByteOffset += pUserData->dwHeaderOffs; + + // The MPQ header should be within range of the file size + if((ByteOffset + MPQ_HEADER_SIZE_V1) < FileSize) + { + // Note: We should verify if there is the MPQ header. + // However, the header could be at any position below that + // that is multiplier of 0x200 + return (TMPQUserData *)pvUserData; + } + } + + return NULL; +} + +// This function gets the right positions of the hash table and the block table. +static DWORD VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize) +{ + TMPQHeader * pHeader = ha->pHeader; + ULONGLONG ByteOffset; + //bool bMalformed = (ha->dwFlags & MPQ_FLAG_MALFORMED) ? true : false; + + // Check the begin of HET table + if(pHeader->HetTablePos64) + { + ByteOffset = ha->MpqPos + pHeader->HetTablePos64; + if(ByteOffset > FileSize) + return ERROR_BAD_FORMAT; + } + + // Check the begin of BET table + if(pHeader->BetTablePos64) + { + ByteOffset = ha->MpqPos + pHeader->BetTablePos64; + if(ByteOffset > FileSize) + return ERROR_BAD_FORMAT; + } + + // Check the begin of hash table + if(pHeader->wHashTablePosHi || pHeader->dwHashTablePos) + { + ByteOffset = FileOffsetFromMpqOffset(ha, MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos)); + if(ByteOffset > FileSize) + return ERROR_BAD_FORMAT; + } + + // Check the begin of block table + if(pHeader->wBlockTablePosHi || pHeader->dwBlockTablePos) + { + ByteOffset = FileOffsetFromMpqOffset(ha, MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos)); + if(ByteOffset > FileSize) + return ERROR_BAD_FORMAT; + } + + // Check the begin of hi-block table + //if(pHeader->HiBlockTablePos64 != 0) + //{ + // ByteOffset = ha->MpqPos + pHeader->HiBlockTablePos64; + // if(ByteOffset > FileSize) + // return ERROR_BAD_FORMAT; + //} + + // All OK. + return ERROR_SUCCESS; +} + +//----------------------------------------------------------------------------- +// Support for alternate markers. Call before opening an archive + +#define SFILE_MARKERS_MIN_SIZE (sizeof(DWORD) + sizeof(DWORD) + sizeof(const char *) + sizeof(const char *)) + +bool WINAPI SFileSetArchiveMarkers(PSFILE_MARKERS pMarkers) +{ + // Check structure minimum size + if(pMarkers == NULL || pMarkers->dwSize < SFILE_MARKERS_MIN_SIZE) + { + SetLastError(ERROR_INVALID_PARAMETER); + return false; + } + + // Make sure that the MPQ cryptography is initialized at this time + InitializeMpqCryptography(); + + // Remember the marker for MPQ header + if(pMarkers->dwSignature != 0) + g_dwMpqSignature = pMarkers->dwSignature; + + // Remember the encryption key for hash table + if(pMarkers->szHashTableKey != NULL) + g_dwHashTableKey = HashString(pMarkers->szHashTableKey, MPQ_HASH_FILE_KEY); + + // Remember the encryption key for block table + if(pMarkers->szBlockTableKey != NULL) + g_dwBlockTableKey = HashString(pMarkers->szBlockTableKey, MPQ_HASH_FILE_KEY); + + // Succeeded + return true; +} + +//----------------------------------------------------------------------------- +// SFileGetLocale and SFileSetLocale +// Set the locale for all newly opened files + +LCID WINAPI SFileGetLocale() +{ + return g_lcFileLocale; +} + +LCID WINAPI SFileSetLocale(LCID lcFileLocale) +{ + return (g_lcFileLocale = lcFileLocale); +} + +//----------------------------------------------------------------------------- +// SFileOpenArchive +// +// szFileName - MPQ archive file name to open +// dwPriority - When SFileOpenFileEx called, this contains the search priority for searched archives +// dwFlags - See MPQ_OPEN_XXX in StormLib.h +// phMpq - Pointer to store open archive handle + +bool WINAPI SFileOpenArchive( + const TCHAR * szMpqName, + DWORD dwPriority, + DWORD dwFlags, + HANDLE * phMpq) +{ + TMPQUserData * pUserData; + TFileStream * pStream = NULL; // Open file stream + TMPQArchive * ha = NULL; // Archive handle + TFileEntry * pFileEntry; + ULONGLONG FileSize = 0; // Size of the file + LPBYTE pbHeaderBuffer = NULL; // Buffer for searching MPQ header + DWORD dwStreamFlags = (dwFlags & STREAM_FLAGS_MASK); + MTYPE MapType = MapTypeNotChecked; + DWORD dwErrCode = ERROR_SUCCESS; + + // Verify the parameters + if(szMpqName == NULL || *szMpqName == 0 || phMpq == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return false; + } + + // One time initialization of MPQ cryptography + InitializeMpqCryptography(); + dwPriority = dwPriority; + + // If not forcing MPQ v 1.0, also use file bitmap + dwStreamFlags |= (dwFlags & MPQ_OPEN_FORCE_MPQ_V1) ? 0 : STREAM_FLAG_USE_BITMAP; + + // Open the MPQ archive file + pStream = FileStream_OpenFile(szMpqName, dwStreamFlags); + if(pStream == NULL) + return false; + + // Check the file size. There must be at least 0x20 bytes + if(dwErrCode == ERROR_SUCCESS) + { + FileStream_GetSize(pStream, &FileSize); + if(FileSize < MPQ_HEADER_SIZE_V1) + dwErrCode = ERROR_BAD_FORMAT; + } + + // Allocate the MPQhandle + if(dwErrCode == ERROR_SUCCESS) + { + if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL) + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; + } + + // Allocate buffer for searching MPQ header + if(dwErrCode == ERROR_SUCCESS) + { + pbHeaderBuffer = STORM_ALLOC(BYTE, HEADER_SEARCH_BUFFER_SIZE); + if(pbHeaderBuffer == NULL) + dwErrCode = ERROR_NOT_ENOUGH_MEMORY; + } + + // Find the position of MPQ header + if(dwErrCode == ERROR_SUCCESS) + { + ULONGLONG ByteOffset = 0; + ULONGLONG EndOfSearch = FileSize; + DWORD dwStrmFlags = 0; + DWORD dwHeaderSize; + DWORD dwHeaderID; + bool bSearchComplete = false; + + memset(ha, 0, sizeof(TMPQArchive)); + ha->dwValidFileFlags = MPQ_FILE_VALID_FLAGS; + ha->pfnHashString = HashStringSlash; + ha->pStream = pStream; + pStream = NULL; + + // Set the archive read only if the stream is read-only + FileStream_GetFlags(ha->pStream, &dwStrmFlags); + ha->dwFlags |= (dwStrmFlags & STREAM_FLAG_READ_ONLY) ? MPQ_FLAG_READ_ONLY : 0; + + // Also remember if we shall check sector CRCs when reading file + ha->dwFlags |= (dwFlags & MPQ_OPEN_CHECK_SECTOR_CRC) ? MPQ_FLAG_CHECK_SECTOR_CRC : 0; + + // Also remember if this MPQ is a patch + ha->dwFlags |= (dwFlags & MPQ_OPEN_PATCH) ? MPQ_FLAG_PATCH : 0; + + // Limit the header searching to about 130 MB of data + if(EndOfSearch > 0x08000000) + EndOfSearch = 0x08000000; + if(FileSize < HEADER_SEARCH_BUFFER_SIZE) + memset(pbHeaderBuffer, 0, HEADER_SEARCH_BUFFER_SIZE); + + // Find the offset of MPQ header within the file + while(bSearchComplete == false && ByteOffset < EndOfSearch) + { + // Always read at least 0x1000 bytes for performance. + // This is what Storm.dll (2002) does. + DWORD dwBytesAvailable = HEADER_SEARCH_BUFFER_SIZE; + + // Cut the bytes available, if needed + if((FileSize - ByteOffset) < HEADER_SEARCH_BUFFER_SIZE) + dwBytesAvailable = (DWORD)(FileSize - ByteOffset); + + // Read the eventual MPQ header + if(!FileStream_Read(ha->pStream, &ByteOffset, pbHeaderBuffer, dwBytesAvailable)) + { + dwErrCode = GetLastError(); + break; + } + + // Check whether the file is AVI file or a Warcraft III/Starcraft II map + if(MapType == MapTypeNotChecked) + { + // Do nothing if the file is an AVI file + if((MapType = CheckMapType(szMpqName, pbHeaderBuffer, dwBytesAvailable)) == MapTypeAviFile) + { + dwErrCode = ERROR_AVI_FILE; + break; + } + } + + // Search the header buffer + for(DWORD dwInBufferOffset = 0; dwInBufferOffset < dwBytesAvailable; dwInBufferOffset += 0x200) + { + // Copy the data from the potential header buffer to the MPQ header + memcpy(ha->HeaderData, pbHeaderBuffer + dwInBufferOffset, sizeof(ha->HeaderData)); + + // If there is the MPQ user data, process it + // Note that Warcraft III does not check for user data, which is abused by many map protectors + dwHeaderID = BSWAP_INT32_UNSIGNED(ha->HeaderData[0]); + if(MapType != MapTypeWarcraft3 && (dwFlags & MPQ_OPEN_FORCE_MPQ_V1) == 0) + { + if(ha->pUserData == NULL && dwHeaderID == ID_MPQ_USERDATA) + { + // Verify if this looks like a valid user data + pUserData = IsValidMpqUserData(ByteOffset, FileSize, ha->HeaderData); + if(pUserData != NULL) + { + // Fill the user data header + ha->UserDataPos = ByteOffset; + ha->pUserData = &ha->UserData; + memcpy(ha->pUserData, pUserData, sizeof(TMPQUserData)); + + // Continue searching from that position + ByteOffset += ha->pUserData->dwHeaderOffs; + break; + } + } + } + + // There must be MPQ header signature. Note that STORM.dll from Warcraft III actually + // tests the MPQ header size. It must be at least 0x20 bytes in order to load it + // Abused by Spazzler Map protector. Note that the size check is not present + // in Storm.dll v 1.00, so Diablo I code would load the MPQ anyway. + dwHeaderSize = BSWAP_INT32_UNSIGNED(ha->HeaderData[1]); + if(dwHeaderID == g_dwMpqSignature && dwHeaderSize >= MPQ_HEADER_SIZE_V1) + { + // Now convert the header to version 4 + dwErrCode = ConvertMpqHeaderToFormat4(ha, ByteOffset, FileSize, dwFlags, MapType); + if(dwErrCode != ERROR_FAKE_MPQ_HEADER) + { + bSearchComplete = true; + break; + } + } + + // Check for MPK archives (Longwu Online - MPQ fork) + if(MapType == MapTypeNotRecognized && dwHeaderID == ID_MPK) + { + // Now convert the MPK header to MPQ Header version 4 + dwErrCode = ConvertMpkHeaderToFormat4(ha, FileSize, dwFlags); + bSearchComplete = true; + break; + } + + // If searching for the MPQ header is disabled, return an error + if(dwFlags & MPQ_OPEN_NO_HEADER_SEARCH) + { + dwErrCode = ERROR_NOT_SUPPORTED; + bSearchComplete = true; + break; + } + + // Move the pointers + ByteOffset += 0x200; + } + } + + // Did we identify one of the supported headers? + if(dwErrCode == ERROR_SUCCESS) + { + // Set the user data position to the MPQ header, if none + if(ha->pUserData == NULL) + ha->UserDataPos = ByteOffset; + + // Set the position of the MPQ header + ha->pHeader = (TMPQHeader *)ha->HeaderData; + ha->MpqPos = ByteOffset; + ha->FileSize = FileSize; + + // Sector size must be nonzero. + if(ByteOffset >= FileSize || ha->pHeader->wSectorSize == 0) + dwErrCode = ERROR_BAD_FORMAT; + } + } + + // Fix table positions according to format + if(dwErrCode == ERROR_SUCCESS) + { + // Dump the header +// DumpMpqHeader(ha->pHeader); + + // W3x Map Protectors use the fact that War3's Storm.dll ignores the MPQ user data, + // and ignores the MPQ format version as well. The trick is to + // fake MPQ format 2, with an improper hi-word position of hash table and block table + // We can overcome such protectors by forcing opening the archive as MPQ v 1.0 + if(dwFlags & MPQ_OPEN_FORCE_MPQ_V1) + { + ha->pHeader->wFormatVersion = MPQ_FORMAT_VERSION_1; + ha->pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V1; + ha->dwFlags |= MPQ_FLAG_READ_ONLY; + ha->pUserData = NULL; + } + + // Anti-overflow. If the hash table size in the header is + // higher than 0x10000000, it would overflow in 32-bit version + // Observed in the malformed Warcraft III maps + // Example map: MPQ_2016_v1_ProtectedMap_TableSizeOverflow.w3x + ha->pHeader->dwBlockTableSize = (ha->pHeader->dwBlockTableSize & BLOCK_INDEX_MASK); + ha->pHeader->dwHashTableSize = (ha->pHeader->dwHashTableSize & BLOCK_INDEX_MASK); + + // Both MPQ_OPEN_NO_LISTFILE or MPQ_OPEN_NO_ATTRIBUTES trigger read only mode + if(dwFlags & (MPQ_OPEN_NO_LISTFILE | MPQ_OPEN_NO_ATTRIBUTES)) + ha->dwFlags |= MPQ_FLAG_READ_ONLY; + + // Check if the caller wants to force adding listfile + if(dwFlags & MPQ_OPEN_FORCE_LISTFILE) + ha->dwFlags |= MPQ_FLAG_LISTFILE_FORCE; + + // StarDat.mpq from Starcraft I BETA: Enable special compression types + if(IsStarcraftBetaArchive(ha->pHeader)) + ha->dwFlags |= MPQ_FLAG_STARCRAFT_BETA; + + // Remember whether whis is a map for Warcraft III + if(MapType == MapTypeWarcraft3) + { + ha->dwValidFileFlags = MPQ_FILE_VALID_FLAGS_W3X; + ha->dwFlags |= MPQ_FLAG_WAR3_MAP; + } + + // If this is starcraft map, set the flag mask + if(MapType == MapTypeStarcraft) + ha->dwValidFileFlags = MPQ_FILE_VALID_FLAGS_SCX; + + // Set the size of file sector + ha->dwSectorSize = (0x200 << ha->pHeader->wSectorSize); + + // Verify if any of the tables doesn't start beyond the end of the file + dwErrCode = VerifyMpqTablePositions(ha, FileSize); + } + + // Read the hash table. Ignore the result, as hash table is no longer required + // Read HET table. Ignore the result, as HET table is no longer required + if(dwErrCode == ERROR_SUCCESS) + { + dwErrCode = LoadAnyHashTable(ha); + } + + // Now, build the file table. It will be built by combining + // the block table, BET table, hi-block table, (attributes) and (listfile). + if(dwErrCode == ERROR_SUCCESS) + { + dwErrCode = BuildFileTable(ha); + } + + // Load the internal listfile and include it to the file table + if(dwErrCode == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_LISTFILE) == 0) + { + // Quick check for (listfile) + pFileEntry = GetFileEntryLocale(ha, LISTFILE_NAME, LANG_NEUTRAL); + if(pFileEntry != NULL) + { + // Ignore result of the operation. (listfile) is optional. + SFileAddListFile((HANDLE)ha, NULL); + ha->dwFileFlags1 = pFileEntry->dwFlags; + } + } + + // Load the "(attributes)" file and merge it to the file table + if(dwErrCode == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_ATTRIBUTES) == 0 && (ha->dwFlags & MPQ_FLAG_BLOCK_TABLE_CUT) == 0) + { + // Quick check for (attributes) + pFileEntry = GetFileEntryLocale(ha, ATTRIBUTES_NAME, LANG_NEUTRAL); + if(pFileEntry != NULL) + { + // Ignore result of the operation. (attributes) is optional. + SAttrLoadAttributes(ha); + ha->dwFileFlags2 = pFileEntry->dwFlags; + } + } + + // Remember whether the archive has weak signature. Only for MPQs format 1.0. + if(dwErrCode == ERROR_SUCCESS) + { + // Quick check for (signature) + pFileEntry = GetFileEntryLocale(ha, SIGNATURE_NAME, LANG_NEUTRAL); + if(pFileEntry != NULL) + { + // Just remember that the archive is weak-signed + assert((pFileEntry->dwFlags & MPQ_FILE_EXISTS) != 0); + ha->dwFileFlags3 = pFileEntry->dwFlags; + } + + // Finally, set the MPQ_FLAG_READ_ONLY if the MPQ was found malformed + ha->dwFlags |= (ha->dwFlags & MPQ_FLAG_MALFORMED) ? MPQ_FLAG_READ_ONLY : 0; + } + + // Cleanup and exit + if(dwErrCode != ERROR_SUCCESS) + { + FileStream_Close(pStream); + FreeArchiveHandle(ha); + SetLastError(dwErrCode); + ha = NULL; + } + + // Free the header buffer + if(pbHeaderBuffer != NULL) + STORM_FREE(pbHeaderBuffer); + if(phMpq != NULL) + *phMpq = ha; + return (dwErrCode == ERROR_SUCCESS); +} + +//----------------------------------------------------------------------------- +// bool WINAPI SFileSetDownloadCallback(HANDLE, SFILE_DOWNLOAD_CALLBACK, void *); +// +// Sets a callback that is called when content is downloaded from the master MPQ +// + +bool WINAPI SFileSetDownloadCallback(HANDLE hMpq, SFILE_DOWNLOAD_CALLBACK DownloadCB, void * pvUserData) +{ + TMPQArchive * ha = (TMPQArchive *)hMpq; + + // Do nothing if 'hMpq' is bad parameter + if(!IsValidMpqHandle(hMpq)) + { + SetLastError(ERROR_INVALID_HANDLE); + return false; + } + + return FileStream_SetCallback(ha->pStream, DownloadCB, pvUserData); +} + +//----------------------------------------------------------------------------- +// bool SFileFlushArchive(HANDLE hMpq) +// +// Saves all dirty data into MPQ archive. +// Has similar effect like SFileCloseArchive, but the archive is not closed. +// Use on clients who keep MPQ archive open even for write operations, +// and terminating without calling SFileCloseArchive might corrupt the archive. +// + +bool WINAPI SFileFlushArchive(HANDLE hMpq) +{ + TMPQArchive * ha; + DWORD dwResultError = ERROR_SUCCESS; + DWORD dwErrCode; + + // Do nothing if 'hMpq' is bad parameter + if((ha = IsValidMpqHandle(hMpq)) == NULL) + { + SetLastError(ERROR_INVALID_HANDLE); + return false; + } + + // Only if the MPQ was changed + if(ha->dwFlags & MPQ_FLAG_CHANGED) + { + // Indicate that we are saving MPQ internal structures + ha->dwFlags |= MPQ_FLAG_SAVING_TABLES; + + // Defragment the file table. This will allow us to put the internal files to the end + DefragmentFileTable(ha); + + // + // Create each internal file + // Note that the (signature) file is usually before (listfile) in the file table + // + + if(ha->dwFlags & MPQ_FLAG_SIGNATURE_NEW) + { + dwErrCode = SSignFileCreate(ha); + if(dwErrCode != ERROR_SUCCESS) + dwResultError = dwErrCode; + } + + if(ha->dwFlags & (MPQ_FLAG_LISTFILE_NEW | MPQ_FLAG_LISTFILE_FORCE)) + { + dwErrCode = SListFileSaveToMpq(ha); + if(dwErrCode != ERROR_SUCCESS) + dwResultError = dwErrCode; + } + + if(ha->dwFlags & MPQ_FLAG_ATTRIBUTES_NEW) + { + dwErrCode = SAttrFileSaveToMpq(ha); + if(dwErrCode != ERROR_SUCCESS) + dwResultError = dwErrCode; + } + + // Save HET table, BET table, hash table, block table, hi-block table + if(ha->dwFlags & MPQ_FLAG_CHANGED) + { + // Rebuild the HET table + if(ha->pHetTable != NULL) + RebuildHetTable(ha); + + // Save all MPQ tables first + dwErrCode = SaveMPQTables(ha); + if(dwErrCode != ERROR_SUCCESS) + dwResultError = dwErrCode; + + // If the archive has weak signature, we need to finish it + if(ha->dwFileFlags3 != 0) + { + dwErrCode = SSignFileFinish(ha); + if(dwErrCode != ERROR_SUCCESS) + dwResultError = dwErrCode; + } + } + + // We are no longer saving internal MPQ structures + ha->dwFlags &= ~MPQ_FLAG_SAVING_TABLES; + } + + // Return the error + if(dwResultError != ERROR_SUCCESS) + SetLastError(dwResultError); + return (dwResultError == ERROR_SUCCESS); +} + +//----------------------------------------------------------------------------- +// bool SFileCloseArchive(HANDLE hMpq); +// + +bool WINAPI SFileCloseArchive(HANDLE hMpq) +{ + TMPQArchive * ha = IsValidMpqHandle(hMpq); + bool bResult = false; + + // Only if the handle is valid + if(ha == NULL) + { + SetLastError(ERROR_INVALID_HANDLE); + return false; + } + + // Invalidate the add file callback so it won't be called + // when saving (listfile) and (attributes) + ha->pfnAddFileCB = NULL; + ha->pvAddFileUserData = NULL; + + // Flush all unsaved data to the storage + bResult = SFileFlushArchive(hMpq); + + // Free all memory used by MPQ archive + FreeArchiveHandle(ha); + return bResult; +} |
