summaryrefslogtreecommitdiff
path: root/StormLib/src/SFileOpenFileEx.cpp
diff options
context:
space:
mode:
authorNicholas Estelami <NEstelami@users.noreply.github.com>2022-06-01 16:05:05 -0400
committerNicholas Estelami <NEstelami@users.noreply.github.com>2022-06-16 20:35:52 -0400
commitb3e299dbdee829c12ada556a1c3aa1f109a0e05b (patch)
treea6e647da673ba7df9389cb1f382a11fc8350bc23 /StormLib/src/SFileOpenFileEx.cpp
parent941c19f2c33a0d1af75a10a64fe0905387d1e440 (diff)
Replaced StormLib .lib files with source code.
Some modifications to handle backslashes and forward slashes, along with some optimizations to speed up OTR generation.
Diffstat (limited to 'StormLib/src/SFileOpenFileEx.cpp')
-rw-r--r--StormLib/src/SFileOpenFileEx.cpp418
1 files changed, 418 insertions, 0 deletions
diff --git a/StormLib/src/SFileOpenFileEx.cpp b/StormLib/src/SFileOpenFileEx.cpp
new file mode 100644
index 000000000..d77f25e35
--- /dev/null
+++ b/StormLib/src/SFileOpenFileEx.cpp
@@ -0,0 +1,418 @@
+/*****************************************************************************/
+/* SFileOpenFileEx.cpp Copyright (c) Ladislav Zezula 2003 */
+/*---------------------------------------------------------------------------*/
+/* Description : */
+/*---------------------------------------------------------------------------*/
+/* Date Ver Who Comment */
+/* -------- ---- --- ------- */
+/* xx.xx.99 1.00 Lad The first version of SFileOpenFileEx.cpp */
+/*****************************************************************************/
+
+#define __STORMLIB_SELF__
+#include "StormLib.h"
+#include "StormCommon.h"
+
+/*****************************************************************************/
+/* Local functions */
+/*****************************************************************************/
+
+static DWORD FindHashIndex(TMPQArchive * ha, DWORD dwFileIndex)
+{
+ TMPQHash * pHashTableEnd;
+ TMPQHash * pHash;
+ DWORD dwFirstIndex = HASH_ENTRY_FREE;
+
+ // Should only be called if the archive has hash table
+ assert(ha->pHashTable != NULL);
+
+ // Multiple hash table entries can point to the file table entry.
+ // We need to search all of them
+ pHashTableEnd = ha->pHashTable + ha->pHeader->dwHashTableSize;
+ for(pHash = ha->pHashTable; pHash < pHashTableEnd; pHash++)
+ {
+ if(MPQ_BLOCK_INDEX(pHash) == dwFileIndex)
+ {
+ // Duplicate hash entry found
+ if(dwFirstIndex != HASH_ENTRY_FREE)
+ return HASH_ENTRY_FREE;
+ dwFirstIndex = (DWORD)(pHash - ha->pHashTable);
+ }
+ }
+
+ // Return the hash table entry index
+ return dwFirstIndex;
+}
+
+static const char * GetPatchFileName(TMPQArchive * ha, const char * szFileName, char * szBuffer)
+{
+ TMPQNamePrefix * pPrefix;
+
+ // Are there patches in the current MPQ?
+ if(ha->dwFlags & MPQ_FLAG_PATCH)
+ {
+ // The patch prefix must be already known here
+ assert(ha->pPatchPrefix != NULL);
+ pPrefix = ha->pPatchPrefix;
+
+ // The patch name for "OldWorld\\XXX\\YYY" is "Base\\XXX\YYY"
+ // We need to remove the "OldWorld\\" prefix
+ if(!_strnicmp(szFileName, "OldWorld\\", 9))
+ szFileName += 9;
+
+ // Create the file name from the known patch entry
+ memcpy(szBuffer, pPrefix->szPatchPrefix, pPrefix->nLength);
+ strcpy(szBuffer + pPrefix->nLength, szFileName);
+ szFileName = szBuffer;
+ }
+
+ return szFileName;
+}
+
+static bool OpenLocalFile(const char * szFileName, HANDLE * PtrFile)
+{
+ TFileStream * pStream;
+ TMPQFile * hf = NULL;
+ TCHAR szFileNameT[MAX_PATH];
+
+ // Convert the file name to UNICODE (if needed)
+ StringCopy(szFileNameT, _countof(szFileNameT), szFileName);
+
+ // Open the file and create the TMPQFile structure
+ pStream = FileStream_OpenFile(szFileNameT, STREAM_FLAG_READ_ONLY);
+ if(pStream != NULL)
+ {
+ // Allocate and initialize file handle
+ hf = CreateFileHandle(NULL, NULL);
+ if(hf != NULL)
+ {
+ hf->pStream = pStream;
+ *PtrFile = hf;
+ return true;
+ }
+ else
+ {
+ FileStream_Close(pStream);
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
+ }
+ }
+ *PtrFile = NULL;
+ return false;
+}
+
+bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, HANDLE * PtrFile)
+{
+ TMPQArchive * haBase = NULL;
+ TMPQArchive * ha = (TMPQArchive *)hMpq;
+ TFileEntry * pFileEntry;
+ TMPQFile * hfPatch; // Pointer to patch file
+ TMPQFile * hfBase = NULL; // Pointer to base open file
+ TMPQFile * hf = NULL;
+ HANDLE hPatchFile;
+ char szNameBuffer[MAX_PATH];
+
+ // First of all, find the latest archive where the file is in base version
+ // (i.e. where the original, unpatched version of the file exists)
+ while(ha != NULL)
+ {
+ // If the file is there, then we remember the archive
+ pFileEntry = GetFileEntryExact(ha, GetPatchFileName(ha, szFileName, szNameBuffer), 0, NULL);
+ if(pFileEntry != NULL && (pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0)
+ haBase = ha;
+
+ // Move to the patch archive
+ ha = ha->haPatch;
+ }
+
+ // If we couldn't find the base file in any of the patches, it doesn't exist
+ if((ha = haBase) != NULL)
+ {
+ // Now open the base file
+ if(SFileOpenFileEx((HANDLE)ha, GetPatchFileName(ha, szFileName, szNameBuffer), SFILE_OPEN_BASE_FILE, (HANDLE *)&hfBase))
+ {
+ // The file must be a base file, i.e. without MPQ_FILE_PATCH_FILE
+ assert((hfBase->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0);
+ hf = hfBase;
+
+ // Now open all patches and attach them on top of the base file
+ for(ha = ha->haPatch; ha != NULL; ha = ha->haPatch)
+ {
+ // Prepare the file name with a correct prefix
+ if(SFileOpenFileEx((HANDLE)ha, GetPatchFileName(ha, szFileName, szNameBuffer), SFILE_OPEN_BASE_FILE, &hPatchFile))
+ {
+ // Remember the new version
+ hfPatch = (TMPQFile *)hPatchFile;
+
+ // We should not find patch file
+ assert((hfPatch->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) != 0);
+
+ // Attach the patch to the base file
+ hf->hfPatch = hfPatch;
+ hf = hfPatch;
+ }
+ }
+ }
+ }
+ else
+ {
+ SetLastError(ERROR_FILE_NOT_FOUND);
+ }
+
+ // Give the updated base MPQ
+ if(PtrFile != NULL)
+ *PtrFile = (HANDLE)hfBase;
+ return (hfBase != NULL);
+}
+
+/*****************************************************************************/
+/* Public functions */
+/*****************************************************************************/
+
+//-----------------------------------------------------------------------------
+// SFileEnumLocales enums all locale versions within MPQ.
+// Functions fills all available language identifiers on a file into the buffer
+// pointed by plcLocales. There must be enough entries to copy the localed,
+// otherwise the function returns ERROR_INSUFFICIENT_BUFFER.
+
+DWORD WINAPI SFileEnumLocales(
+ HANDLE hMpq,
+ const char * szFileName,
+ LCID * PtrLocales,
+ LPDWORD PtrMaxLocales,
+ DWORD dwSearchScope)
+{
+ TMPQArchive * ha = (TMPQArchive *)hMpq;
+ TMPQHash * pFirstHash;
+ TMPQHash * pHash;
+ DWORD dwFileIndex = 0;
+ DWORD dwMaxLocales;
+ DWORD dwLocales = 0;
+
+ // Test the parameters
+ if(!IsValidMpqHandle(hMpq))
+ return ERROR_INVALID_HANDLE;
+ if(szFileName == NULL || *szFileName == 0)
+ return ERROR_INVALID_PARAMETER;
+ if(ha->pHashTable == NULL)
+ return ERROR_NOT_SUPPORTED;
+ if(PtrMaxLocales == NULL)
+ return ERROR_INVALID_PARAMETER;
+ if(IsPseudoFileName(szFileName, &dwFileIndex))
+ return ERROR_INVALID_PARAMETER;
+
+ // Keep compiler happy
+ dwMaxLocales = PtrMaxLocales[0];
+ dwSearchScope = dwSearchScope;
+
+ // Parse all files with that name
+ pFirstHash = pHash = GetFirstHashEntry(ha, szFileName);
+ while(pHash != NULL)
+ {
+ // Put the locales to the buffer
+ if(PtrLocales != NULL && dwLocales < dwMaxLocales)
+ *PtrLocales++ = pHash->lcLocale;
+ dwLocales++;
+
+ // Get the next locale
+ pHash = GetNextHashEntry(ha, pFirstHash, pHash);
+ }
+
+ // Give the caller the number of locales and return
+ PtrMaxLocales[0] = dwLocales;
+ return (dwLocales <= dwMaxLocales) ? ERROR_SUCCESS : ERROR_INSUFFICIENT_BUFFER;
+}
+
+//-----------------------------------------------------------------------------
+// SFileOpenFileEx
+//
+// hMpq - Handle of opened MPQ archive
+// szFileName - Name of file to open
+// dwSearchScope - Where to search
+// PtrFile - Pointer to store opened file handle
+
+bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * PtrFile)
+{
+ TMPQArchive * ha = IsValidMpqHandle(hMpq);
+ TFileEntry * pFileEntry = NULL;
+ TMPQFile * hf = NULL;
+ DWORD dwHashIndex = HASH_ENTRY_FREE;
+ DWORD dwFileIndex = 0;
+ DWORD dwErrCode = ERROR_SUCCESS;
+ bool bOpenByIndex = false;
+
+ // Don't accept NULL pointer to file handle
+ if(szFileName == NULL || *szFileName == 0)
+ dwErrCode = ERROR_INVALID_PARAMETER;
+
+ // When opening a file from MPQ, the handle must be valid
+ if(dwSearchScope != SFILE_OPEN_LOCAL_FILE && ha == NULL)
+ dwErrCode = ERROR_INVALID_HANDLE;
+
+ // When not checking for existence, the pointer to file handle must be valid
+ if(dwSearchScope != SFILE_OPEN_CHECK_EXISTS && PtrFile == NULL)
+ dwErrCode = ERROR_INVALID_PARAMETER;
+
+ // Prepare the file opening
+ if(dwErrCode == ERROR_SUCCESS)
+ {
+ switch(dwSearchScope)
+ {
+ case SFILE_OPEN_FROM_MPQ:
+ case SFILE_OPEN_BASE_FILE:
+ case SFILE_OPEN_CHECK_EXISTS:
+
+ // If this MPQ has no patches, open the file from this MPQ directly
+ if(ha->haPatch == NULL || dwSearchScope == SFILE_OPEN_BASE_FILE)
+ {
+ pFileEntry = GetFileEntryLocale2(ha, szFileName, g_lcFileLocale, &dwHashIndex);
+ }
+
+ // If this MPQ is a patched archive, open the file as patched
+ else
+ {
+ return OpenPatchedFile(hMpq, szFileName, PtrFile);
+ }
+ break;
+
+ case SFILE_OPEN_ANY_LOCALE:
+
+ // This open option is reserved for opening MPQ internal listfile.
+ // No argument validation. Tries to open file with neutral locale first,
+ // then any other available.
+ pFileEntry = GetFileEntryLocale2(ha, szFileName, 0, &dwHashIndex);
+ break;
+
+ case SFILE_OPEN_LOCAL_FILE:
+
+ // Open a local file
+ return OpenLocalFile(szFileName, PtrFile);
+
+ default:
+
+ // Don't accept any other value
+ dwErrCode = ERROR_INVALID_PARAMETER;
+ break;
+ }
+ }
+
+ // Check whether the file really exists in the MPQ
+ if(dwErrCode == ERROR_SUCCESS)
+ {
+ // If we didn't find the file, try to open it using pseudo file name ("File
+ if (pFileEntry == NULL || (pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0)
+ {
+ // Check the pseudo-file name ("File00000001.ext")
+ if ((bOpenByIndex = IsPseudoFileName(szFileName, &dwFileIndex)) == true)
+ {
+ // Get the file entry for the file
+ if (dwFileIndex < ha->dwFileTableSize)
+ {
+ pFileEntry = ha->pFileTable + dwFileIndex;
+ }
+ }
+
+ // Still not found?
+ if (pFileEntry == NULL)
+ {
+ dwErrCode = ERROR_FILE_NOT_FOUND;
+ }
+ }
+
+ // Perform some checks of invalid files
+ if (pFileEntry != NULL)
+ {
+ // MPQ protectors use insanely amount of fake files, often with very high size.
+ // We won't open any files whose compressed size is bigger than archive size
+ // If the file is not compressed, its size cannot be bigger than archive size
+ if ((pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) == 0 && (pFileEntry->dwFileSize > ha->FileSize))
+ {
+ dwErrCode = ERROR_FILE_CORRUPT;
+ pFileEntry = NULL;
+ }
+
+ // Ignore unknown loading flags (example: MPQ_2016_v1_WME4_4.w3x)
+// if(pFileEntry->dwFlags & ~MPQ_FILE_VALID_FLAGS)
+// {
+// dwErrCode = ERROR_NOT_SUPPORTED;
+// pFileEntry = NULL;
+// }
+ }
+ }
+
+ // Did the caller just wanted to know if the file exists?
+ if(dwErrCode == ERROR_SUCCESS && dwSearchScope != SFILE_OPEN_CHECK_EXISTS)
+ {
+ // Allocate file handle
+ hf = CreateFileHandle(ha, pFileEntry);
+ if(hf != NULL)
+ {
+ // Get the hash index for the file
+ if(ha->pHashTable != NULL && dwHashIndex == HASH_ENTRY_FREE)
+ dwHashIndex = FindHashIndex(ha, dwFileIndex);
+ if(dwHashIndex != HASH_ENTRY_FREE)
+ hf->pHashEntry = ha->pHashTable + dwHashIndex;
+ hf->dwHashIndex = dwHashIndex;
+
+ // If the MPQ has sector CRC enabled, enable if for the file
+ if(ha->dwFlags & MPQ_FLAG_CHECK_SECTOR_CRC)
+ hf->bCheckSectorCRCs = true;
+
+ // If we know the real file name, copy it to the file entry
+ if(bOpenByIndex == false)
+ {
+ // If there is no file name yet, allocate it
+ AllocateFileName(ha, pFileEntry, szFileName);
+
+ // If the file is encrypted, we should detect the file key
+ if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
+ {
+ hf->dwFileKey = DecryptFileKey(szFileName,
+ pFileEntry->ByteOffset,
+ pFileEntry->dwFileSize,
+ pFileEntry->dwFlags);
+ }
+ }
+ }
+ else
+ {
+ dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
+ }
+ }
+
+ // Give the file entry
+ if(PtrFile != NULL)
+ PtrFile[0] = hf;
+
+ // Return error code
+ if(dwErrCode != ERROR_SUCCESS)
+ SetLastError(dwErrCode);
+ return (dwErrCode == ERROR_SUCCESS);
+}
+
+//-----------------------------------------------------------------------------
+// SFileHasFile
+//
+// hMpq - Handle of opened MPQ archive
+// szFileName - Name of file to look for
+
+bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName)
+{
+ return SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_CHECK_EXISTS, NULL);
+}
+
+//-----------------------------------------------------------------------------
+// bool WINAPI SFileCloseFile(HANDLE hFile);
+
+bool WINAPI SFileCloseFile(HANDLE hFile)
+{
+ TMPQFile * hf = (TMPQFile *)hFile;
+
+ if(!IsValidFileHandle(hFile))
+ {
+ SetLastError(ERROR_INVALID_HANDLE);
+ return false;
+ }
+
+ // Free the structure
+ FreeFileHandle(hf);
+ return true;
+}