diff --git a/3rdParty/StormLib/README b/3rdParty/StormLib/README index 9493cfcaf..89d6faab1 100644 --- a/3rdParty/StormLib/README +++ b/3rdParty/StormLib/README @@ -1,13 +1,6 @@ -This is a slightly modified version of ladislav-zezula/StormLib@4ad0bff21da0163917e1552960e6a43679586b89 - -Cosmetic changes: -* Remove all unused files. -* Comment out unnecessary bits with `#ifndef FULL`. - -Implementation changes: -* Redefine `bool` to `BOOL`. -* Use STORMAPI instead of WINAPI for calling convention (and define it as nothing). -* Use external Storm error handling (`SErrGetLastError()` etc.) -* Convert pathnames to Unix-style slashes. -* Bugfixes, with comment: // BUGFIX (devilutionX) -* Fix missing header includes on macOS (devilutionX) +This is a slightly modified version of ladislav-zezula/StormLib@0a9057a09f083eb4791a55eb3d9538cea9822e37 + +Changes: + +* Removed all files not used by DevilutionX. +* Comment out unnecessary bits with `#ifndef FULL`. diff --git a/3rdParty/StormLib/src/FileStream.cpp b/3rdParty/StormLib/src/FileStream.cpp index 47544e1d4..7268923df 100644 --- a/3rdParty/StormLib/src/FileStream.cpp +++ b/3rdParty/StormLib/src/FileStream.cpp @@ -17,16 +17,6 @@ #include "StormLib.h" #include "StormCommon.h" #include "FileStream.h" - -#ifdef __vita__ -#include -#define mmap(ptr, size, c, d, e, f) malloc(size) -#define munmap(ptr, size) free(ptr) -#define PROT_READ 0 -#define MAP_PRIVATE 0 -#endif - - #ifdef _MSC_VER #pragma comment(lib, "wininet.lib") // Internet functions for HTTP stream @@ -43,8 +33,7 @@ //----------------------------------------------------------------------------- // Local functions - platform-specific functions -#ifdef FULL -#ifndef PLATFORM_WINDOWS +#ifndef STORMLIB_WINDOWS static DWORD nLastError = ERROR_SUCCESS; DWORD GetLastError() @@ -52,12 +41,11 @@ DWORD GetLastError() return nLastError; } -void SetLastError(DWORD nError) +void SetLastError(DWORD dwErrCode) { - nLastError = nError; + nLastError = dwErrCode; } #endif -#endif static DWORD StringToInt(const char * szString) { @@ -65,13 +53,29 @@ static DWORD StringToInt(const char * szString) while('0' <= szString[0] && szString[0] <= '9') { - dwValue = (dwValue * 10) + (szString[0] - '9'); + dwValue = (dwValue * 10) + (szString[0] - '0'); szString++; } return dwValue; } +static void CreateNameWithSuffix(LPTSTR szBuffer, size_t cchMaxChars, LPCTSTR szName, unsigned int nValue) +{ + LPTSTR szBufferEnd = szBuffer + cchMaxChars - 1; + + // Copy the name + while(szBuffer < szBufferEnd && szName[0] != 0) + *szBuffer++ = *szName++; + + // Append "." + if(szBuffer < szBufferEnd) + *szBuffer++ = '.'; + + // Append the number + IntToString(szBuffer, szBufferEnd - szBuffer + 1, nValue); +} + //----------------------------------------------------------------------------- // Dummy init function @@ -85,7 +89,7 @@ static void BaseNone_Init(TFileStream *) static bool BaseFile_Create(TFileStream * pStream) { -#ifdef PLATFORM_WINDOWS +#ifdef STORMLIB_WINDOWS { DWORD dwWriteShare = (pStream->dwFlags & STREAM_FLAG_WRITE_SHARE) ? FILE_SHARE_WRITE : 0; @@ -101,15 +105,15 @@ static bool BaseFile_Create(TFileStream * pStream) } #endif -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) || defined(PLATFORM_HAIKU) || defined(PLATFORM_AMIGA) || defined(PLATFORM_SWITCH) || defined(PLATFORM_CTR) || defined(PLATFORM_VITA) +#if defined(STORMLIB_MAC) || defined(STORMLIB_LINUX) { intptr_t handle; handle = open(pStream->szFileName, O_RDWR | O_CREAT | O_TRUNC | O_LARGEFILE, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if(handle == -1) { + pStream->Base.File.hFile = INVALID_HANDLE_VALUE; nLastError = errno; - pStream->Base.File.hFile = INVALID_HANDLE_VALUE; // BUGFIX (devilutionX) return false; } @@ -125,7 +129,7 @@ static bool BaseFile_Create(TFileStream * pStream) static bool BaseFile_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD dwStreamFlags) { -#ifdef PLATFORM_WINDOWS +#ifdef STORMLIB_WINDOWS { ULARGE_INTEGER FileSize; DWORD dwWriteAccess = (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? 0 : FILE_WRITE_DATA | FILE_APPEND_DATA | FILE_WRITE_ATTRIBUTES; @@ -151,7 +155,7 @@ static bool BaseFile_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD } #endif -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) || defined(PLATFORM_HAIKU) || defined(PLATFORM_AMIGA) || defined(PLATFORM_SWITCH) || defined(PLATFORM_CTR) || defined(PLATFORM_VITA) +#if defined(STORMLIB_MAC) || defined(STORMLIB_LINUX) { struct stat64 fileinfo; int oflag = (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? O_RDONLY : O_RDWR; @@ -161,17 +165,17 @@ static bool BaseFile_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD handle = open(szFileName, oflag | O_LARGEFILE); if(handle == -1) { + pStream->Base.File.hFile = INVALID_HANDLE_VALUE; nLastError = errno; - pStream->Base.File.hFile = INVALID_HANDLE_VALUE; // BUGFIX (devilutionX) return false; } // Get the file size if(fstat64(handle, &fileinfo) == -1) { + pStream->Base.File.hFile = INVALID_HANDLE_VALUE; nLastError = errno; close(handle); - pStream->Base.File.hFile = INVALID_HANDLE_VALUE; // BUGFIX (devilutionX) return false; } @@ -198,7 +202,7 @@ static bool BaseFile_Read( ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.File.FilePos; DWORD dwBytesRead = 0; // Must be set by platform-specific code -#ifdef PLATFORM_WINDOWS +#ifdef STORMLIB_WINDOWS { // Note: StormLib no longer supports Windows 9x. // Thus, we can use the OVERLAPPED structure to specify @@ -222,7 +226,7 @@ static bool BaseFile_Read( } #endif -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) || defined(PLATFORM_HAIKU) || defined(PLATFORM_AMIGA) || defined(PLATFORM_SWITCH) || defined(PLATFORM_CTR) || defined(PLATFORM_VITA) +#if defined(STORMLIB_MAC) || defined(STORMLIB_LINUX) { ssize_t bytes_read; @@ -269,7 +273,7 @@ static bool BaseFile_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.File.FilePos; DWORD dwBytesWritten = 0; // Must be set by platform-specific code -#ifdef PLATFORM_WINDOWS +#ifdef STORMLIB_WINDOWS { // Note: StormLib no longer supports Windows 9x. // Thus, we can use the OVERLAPPED structure to specify @@ -293,7 +297,7 @@ static bool BaseFile_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const } #endif -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) || defined(PLATFORM_HAIKU) || defined(PLATFORM_AMIGA) || defined(PLATFORM_SWITCH) || defined(PLATFORM_CTR) || defined(PLATFORM_VITA) +#if defined(STORMLIB_MAC) || defined(STORMLIB_LINUX) { ssize_t bytes_written; @@ -335,7 +339,7 @@ static bool BaseFile_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const */ static bool BaseFile_Resize(TFileStream * pStream, ULONGLONG NewFileSize) { -#ifdef PLATFORM_WINDOWS +#ifdef STORMLIB_WINDOWS { LONG FileSizeHi = (LONG)(NewFileSize >> 32); LONG FileSizeLo; @@ -360,7 +364,7 @@ static bool BaseFile_Resize(TFileStream * pStream, ULONGLONG NewFileSize) } #endif -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) || defined(PLATFORM_HAIKU) || defined(PLATFORM_AMIGA) || defined(PLATFORM_SWITCH) || defined(PLATFORM_CTR) || defined(PLATFORM_VITA) +#if defined(STORMLIB_MAC) || defined(STORMLIB_LINUX) { if(ftruncate64((intptr_t)pStream->Base.File.hFile, (off64_t)NewFileSize) == -1) { @@ -395,7 +399,7 @@ static bool BaseFile_GetPos(TFileStream * pStream, ULONGLONG * pByteOffset) // Renames the file pointed by pStream so that it contains data from pNewStream static bool BaseFile_Replace(TFileStream * pStream, TFileStream * pNewStream) { -#ifdef PLATFORM_WINDOWS +#ifdef STORMLIB_WINDOWS // Delete the original stream file. Don't check the result value, // because if the file doesn't exist, it would fail DeleteFile(pStream->szFileName); @@ -404,7 +408,7 @@ static bool BaseFile_Replace(TFileStream * pStream, TFileStream * pNewStream) return (bool)MoveFile(pNewStream->szFileName, pStream->szFileName); #endif -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) || defined(PLATFORM_HAIKU) || defined(PLATFORM_AMIGA) || defined(PLATFORM_SWITCH) || defined(PLATFORM_CTR) || defined(PLATFORM_VITA) +#if defined(STORMLIB_MAC) || defined(STORMLIB_LINUX) // "rename" on Linux also works if the target file exists if(rename(pNewStream->szFileName, pStream->szFileName) == -1) { @@ -420,11 +424,11 @@ static void BaseFile_Close(TFileStream * pStream) { if(pStream->Base.File.hFile != INVALID_HANDLE_VALUE) { -#ifdef PLATFORM_WINDOWS +#ifdef STORMLIB_WINDOWS CloseHandle(pStream->Base.File.hFile); #endif -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) || defined(PLATFORM_HAIKU) || defined(PLATFORM_AMIGA) || defined(PLATFORM_SWITCH) || defined(PLATFORM_CTR) || defined(PLATFORM_VITA) +#if defined(STORMLIB_MAC) || defined(STORMLIB_LINUX) close((intptr_t)pStream->Base.File.hFile); #endif } @@ -449,61 +453,119 @@ static void BaseFile_Init(TFileStream * pStream) //----------------------------------------------------------------------------- // Local functions - base memory-mapped file support -static bool BaseMap_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD dwStreamFlags) +#ifdef STORMLIB_WINDOWS + +typedef struct _SECTION_BASIC_INFORMATION +{ + PVOID BaseAddress; + ULONG Attributes; + LARGE_INTEGER Size; +} SECTION_BASIC_INFORMATION, *PSECTION_BASIC_INFORMATION; + +typedef ULONG (WINAPI * NTQUERYSECTION)( + IN HANDLE SectionHandle, + IN ULONG SectionInformationClass, + OUT PVOID SectionInformation, + IN SIZE_T Length, + OUT PSIZE_T ResultLength); + +static bool RetrieveFileMappingSize(HANDLE hSection, ULARGE_INTEGER & RefFileSize) { -#ifdef PLATFORM_WINDOWS + SECTION_BASIC_INFORMATION BasicInfo = {0}; + NTQUERYSECTION PfnQuerySection; + HMODULE hNtdll; + SIZE_T ReturnLength = 0; + + if((hNtdll = GetModuleHandle(_T("ntdll.dll"))) != NULL) + { + PfnQuerySection = (NTQUERYSECTION)GetProcAddress(hNtdll, "NtQuerySection"); + if(PfnQuerySection != NULL) + { + if(PfnQuerySection(hSection, 0, &BasicInfo, sizeof(SECTION_BASIC_INFORMATION), &ReturnLength) == 0) + { + RefFileSize.HighPart = BasicInfo.Size.HighPart; + RefFileSize.LowPart = BasicInfo.Size.LowPart; + return true; + } + } + } - ULARGE_INTEGER FileSize; - HANDLE hFile; - HANDLE hMap; + return false; +} +#endif + +static bool BaseMap_Open(TFileStream * pStream, LPCTSTR szFileName, DWORD dwStreamFlags) +{ +#ifdef STORMLIB_WINDOWS + + ULARGE_INTEGER FileSize = {0}; + HANDLE hFile = INVALID_HANDLE_VALUE; + HANDLE hMap = NULL; bool bResult = false; // Keep compiler happy dwStreamFlags = dwStreamFlags; - // Open the file for read access - hFile = CreateFile(szFileName, FILE_READ_DATA, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); - if(hFile != INVALID_HANDLE_VALUE) + // 1) Try to treat "szFileName" as a section name + hMap = OpenFileMapping(SECTION_QUERY | FILE_MAP_READ, FALSE, szFileName); + if(hMap != NULL) { - // Retrieve file size. Don't allow mapping file of a zero size. - FileSize.LowPart = GetFileSize(hFile, &FileSize.HighPart); - if(FileSize.QuadPart != 0) + // Try to retrieve the size of the mapping + if(!RetrieveFileMappingSize(hMap, FileSize)) { - // Now create mapping object - hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); - if(hMap != NULL) + CloseHandle(hMap); + hMap = NULL; + } + } + + // 2) Treat the name as file name + else + { + hFile = CreateFile(szFileName, FILE_READ_DATA, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); + if(hFile != INVALID_HANDLE_VALUE) + { + // Retrieve file size. Don't allow mapping file of a zero size. + FileSize.LowPart = GetFileSize(hFile, &FileSize.HighPart); + if(FileSize.QuadPart != 0) { - // Map the entire view into memory - // Note that this operation will fail if the file can't fit - // into usermode address space - pStream->Base.Map.pbFile = (LPBYTE)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0); - if(pStream->Base.Map.pbFile != NULL) - { - // Retrieve file time - GetFileTime(hFile, NULL, NULL, (LPFILETIME)&pStream->Base.Map.FileTime); + // Now create file mapping over the file + hMap = CreateFileMapping(hFile, NULL, PAGE_READONLY, 0, 0, NULL); + } + } + } - // Retrieve file size and position - pStream->Base.Map.FileSize = FileSize.QuadPart; - pStream->Base.Map.FilePos = 0; - bResult = true; - } + // Did it succeed? + if(hMap != NULL) + { + // Map the entire view into memory + // Note that this operation will fail if the file can't fit + // into usermode address space + pStream->Base.Map.pbFile = (LPBYTE)MapViewOfFile(hMap, FILE_MAP_READ, 0, 0, 0); + if(pStream->Base.Map.pbFile != NULL) + { + // Retrieve file time. If it's named section, put 0 + if(hFile != INVALID_HANDLE_VALUE) + GetFileTime(hFile, NULL, NULL, (LPFILETIME)&pStream->Base.Map.FileTime); - // Close the map handle - CloseHandle(hMap); - } + // Retrieve file size and position + pStream->Base.Map.FileSize = FileSize.QuadPart; + pStream->Base.Map.FilePos = 0; + bResult = true; } - // Close the file handle - CloseHandle(hFile); + // Close the map handle + CloseHandle(hMap); } - // If the file is not there and is not available for random access, - // report error - if(bResult == false) - return false; -#endif + // Close the file handle + if(hFile != INVALID_HANDLE_VALUE) + CloseHandle(hFile); + + // Return the result of the operation + return bResult; + +#elif defined(STORMLIB_HAS_MMAP) -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) || defined(PLATFORM_HAIKU) || defined(PLATFORM_AMIGA) || defined(PLATFORM_SWITCH) || defined(PLATFORM_CTR) || defined(PLATFORM_VITA) struct stat64 fileinfo; intptr_t handle; bool bResult = false; @@ -515,12 +577,7 @@ static bool BaseMap_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD // Get the file size if(fstat64(handle, &fileinfo) != -1) { -#if !defined(PLATFORM_AMIGA) -#if defined(PLATFORM_SWITCH) || defined(PLATFORM_CTR) || defined(PLATFORM_VITA) - pStream->Base.Map.pbFile = (LPBYTE)malloc((size_t)fileinfo.st_size); -#else pStream->Base.Map.pbFile = (LPBYTE)mmap(NULL, (size_t)fileinfo.st_size, PROT_READ, MAP_PRIVATE, handle, 0); -#endif if(pStream->Base.Map.pbFile != NULL) { // time_t is number of seconds since 1.1.1970, UTC. @@ -531,20 +588,21 @@ static bool BaseMap_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD pStream->Base.Map.FilePos = 0; bResult = true; } -#endif } close(handle); } // Did the mapping fail? if(bResult == false) - { nLastError = errno; - return false; - } -#endif + return bResult; - return true; +#else + + // File mapping is not supported + return false; + +#endif } static bool BaseMap_Read( @@ -573,18 +631,17 @@ static bool BaseMap_Read( static void BaseMap_Close(TFileStream * pStream) { -#ifdef PLATFORM_WINDOWS + +#ifdef STORMLIB_WINDOWS + if(pStream->Base.Map.pbFile != NULL) UnmapViewOfFile(pStream->Base.Map.pbFile); -#endif -#if (defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) || defined(PLATFORM_HAIKU)) && !defined(PLATFORM_AMIGA) && !defined(PLATFORM_SWITCH) && !defined(PLATFORM_CTR) && !defined(PLATFORM_VITA) - //Todo(Amiga): Fix a proper solution for this +#elif defined(STORMLIB_HAS_MMAP) + if(pStream->Base.Map.pbFile != NULL) munmap(pStream->Base.Map.pbFile, (size_t )pStream->Base.Map.FileSize); -#elif defined(PLATFORM_SWITCH) || defined(PLATFORM_CTR) || defined(PLATFORM_VITA) - if(pStream->Base.Map.pbFile != NULL) - free(pStream->Base.Map.pbFile); + #endif pStream->Base.Map.pbFile = NULL; @@ -632,7 +689,7 @@ static const TCHAR * BaseHttp_ExtractServerName(const TCHAR * szFileName, TCHAR static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD dwStreamFlags) { -#ifdef PLATFORM_WINDOWS +#ifdef STORMLIB_WINDOWS HINTERNET hRequest; DWORD dwTemp = 0; @@ -677,6 +734,19 @@ static bool BaseHttp_Open(TFileStream * pStream, const TCHAR * szFileName, DWORD DWORD dwFileSize = 0; DWORD dwDataSize; DWORD dwIndex = 0; + TCHAR StatusCode[0x08]; + + // Check if the file succeeded to open + dwDataSize = sizeof(StatusCode); + if(HttpQueryInfo(hRequest, HTTP_QUERY_STATUS_CODE, StatusCode, &dwDataSize, &dwIndex)) + { + if(_tcscmp(StatusCode, _T("200"))) + { + InternetCloseHandle(hRequest); + SetLastError(ERROR_FILE_NOT_FOUND); + return false; + } + } // Check if the MPQ has Last Modified field dwDataSize = sizeof(ULONGLONG); @@ -731,7 +801,7 @@ static bool BaseHttp_Read( void * pvBuffer, // Pointer to data to be read DWORD dwBytesToRead) // Number of bytes to read from the file { -#ifdef PLATFORM_WINDOWS +#ifdef STORMLIB_WINDOWS ULONGLONG ByteOffset = (pByteOffset != NULL) ? *pByteOffset : pStream->Base.Http.FilePos; DWORD dwTotalBytesRead = 0; @@ -752,8 +822,8 @@ static bool BaseHttp_Read( { // Add range request to the HTTP headers // http://www.clevercomponents.com/articles/article015/resuming.asp - _stprintf(szRangeRequest, _T("Range: bytes=%u-%u"), (unsigned int)dwStartOffset, (unsigned int)dwEndOffset); - HttpAddRequestHeaders(hRequest, szRangeRequest, 0xFFFFFFFF, HTTP_ADDREQ_FLAG_ADD_IF_NEW); + wsprintf(szRangeRequest, _T("Range: bytes=%u-%u"), (unsigned int)dwStartOffset, (unsigned int)dwEndOffset); + HttpAddRequestHeaders(hRequest, szRangeRequest, 0xFFFFFFFF, HTTP_ADDREQ_FLAG_ADD_IF_NEW); // Send the request to the server if(HttpSendRequest(hRequest, NULL, 0, NULL, 0)) @@ -804,7 +874,7 @@ static bool BaseHttp_Read( static void BaseHttp_Close(TFileStream * pStream) { -#ifdef PLATFORM_WINDOWS +#ifdef STORMLIB_WINDOWS if(pStream->Base.Http.hConnect != NULL) InternetCloseHandle(pStream->Base.Http.hConnect); pStream->Base.Http.hConnect = NULL; @@ -1006,7 +1076,7 @@ static void BlockStream_Close(TBlockStream * pStream) static STREAM_INIT StreamBaseInit[4] = { BaseFile_Init, - BaseMap_Init, + BaseMap_Init, BaseHttp_Init, BaseNone_Init }; @@ -1690,7 +1760,7 @@ static void PartStream_Close(TBlockStream * pStream) // Make sure that the header is properly BSWAPed BSWAP_ARRAY32_UNSIGNED(&PartHeader, sizeof(PART_FILE_HEADER)); - sprintf(PartHeader.GameBuildNumber, "%u", (unsigned int)pStream->BuildNumber); + IntToString(PartHeader.GameBuildNumber, _countof(PartHeader.GameBuildNumber), pStream->BuildNumber); // Write the part header pStream->BaseWrite(pStream, &ByteOffset, &PartHeader, sizeof(PART_FILE_HEADER)); @@ -2049,7 +2119,7 @@ static bool MpqeStream_DetectFileKey(TEncryptedStream * pStream) // Prepare they decryption key from game serial number CreateKeyFromAuthCode(pStream->Key, AuthCodeArray[i]); - // Try to decrypt with the given key + // Try to decrypt with the given key memcpy(FileHeader, EncryptedHeader, MPQE_CHUNK_SIZE); DecryptFileChunk((LPDWORD)FileHeader, pStream->Key, ByteOffset, MPQE_CHUNK_SIZE); @@ -2275,7 +2345,7 @@ static TFileStream * Block4Stream_Open(const TCHAR * szFileName, DWORD dwStreamF for(int nSuffix = 0; nSuffix < 30; nSuffix++) { // Open the n-th file - _stprintf(szNameBuff, _T("%s.%u"), pStream->szFileName, nSuffix); + CreateNameWithSuffix(szNameBuff, nNameLength + 4, pStream->szFileName, nSuffix); if(!pStream->BaseOpen(pStream, szNameBuff, dwBaseFlags)) break; @@ -2305,7 +2375,7 @@ static TFileStream * Block4Stream_Open(const TCHAR * szFileName, DWORD dwStreamF RemainderBlock = FileSize % (BLOCK4_BLOCK_SIZE + BLOCK4_HASH_SIZE); BlockCount = FileSize / (BLOCK4_BLOCK_SIZE + BLOCK4_HASH_SIZE); - // Increment the stream size and number of blocks + // Increment the stream size and number of blocks pStream->StreamSize += (BlockCount * BLOCK4_BLOCK_SIZE); pStream->BlockCount += (DWORD)BlockCount; @@ -2839,7 +2909,7 @@ void FileStream_Close(TFileStream * pStream) FileStream_Close(pStream->pMaster); pStream->pMaster = NULL; - // Close the stream provider ... + // Close the stream provider if(pStream->StreamClose != NULL) pStream->StreamClose(pStream); diff --git a/3rdParty/StormLib/src/FileStream.h b/3rdParty/StormLib/src/FileStream.h index 44beeed91..2bc118b28 100644 --- a/3rdParty/StormLib/src/FileStream.h +++ b/3rdParty/StormLib/src/FileStream.h @@ -202,7 +202,7 @@ struct TBlockStream : public TFileStream DWORD BlockCount; // Number of data blocks in the file DWORD IsComplete; // If nonzero, no blocks are missing DWORD IsModified; // nonzero if the bitmap has been modified -}; +}; //----------------------------------------------------------------------------- // Structure for encrypted stream diff --git a/3rdParty/StormLib/src/SBaseCommon.cpp b/3rdParty/StormLib/src/SBaseCommon.cpp index 4ff2cfe39..2f30a712e 100644 --- a/3rdParty/StormLib/src/SBaseCommon.cpp +++ b/3rdParty/StormLib/src/SBaseCommon.cpp @@ -20,7 +20,10 @@ char StormLibCopyright[] = "StormLib v " STORMLIB_VERSION_STRING " Copyright Lad //----------------------------------------------------------------------------- // Local variables -LCID lcFileLocale = LANG_NEUTRAL; // File locale +DWORD g_dwMpqSignature = ID_MPQ; // Marker for MPQ header +DWORD g_dwHashTableKey = MPQ_KEY_HASH_TABLE; // Key for hash table +DWORD g_dwBlockTableKey = MPQ_KEY_BLOCK_TABLE; // Key for block table +LCID g_lcFileLocale = LANG_NEUTRAL; // File locale USHORT wPlatform = 0; // File platform //----------------------------------------------------------------------------- @@ -95,11 +98,13 @@ unsigned char AsciiToUpperTable_Slash[256] = //----------------------------------------------------------------------------- // Safe string functions (for ANSI builds) -void StringCopy(char * szTarget, size_t cchTarget, const char * szSource) +char * StringCopy(char * szTarget, size_t cchTarget, const char * szSource) { + size_t cchSource = 0; + if(cchTarget > 0) { - size_t cchSource = strlen(szSource); + cchSource = strlen(szSource); if(cchSource >= cchTarget) cchSource = cchTarget - 1; @@ -107,6 +112,8 @@ void StringCopy(char * szTarget, size_t cchTarget, const char * szSource) memcpy(szTarget, szSource, cchSource); szTarget[cchSource] = 0; } + + return szTarget + cchSource; } void StringCat(char * szTarget, size_t cchTargetMax, const char * szSource) @@ -121,6 +128,26 @@ void StringCat(char * szTarget, size_t cchTargetMax, const char * szSource) } } +void StringCreatePseudoFileName(char * szBuffer, size_t cchMaxChars, unsigned int nIndex, const char * szExtension) +{ + char * szBufferEnd = szBuffer + cchMaxChars; + + // "File" + szBuffer = StringCopy(szBuffer, (szBufferEnd - szBuffer), "File"); + + // Number + szBuffer = IntToString(szBuffer, szBufferEnd - szBuffer + 1, nIndex, 8); + + // Dot + if(szBuffer < szBufferEnd) + *szBuffer++ = '.'; + + // Extension + while(szExtension[0] == '.') + szExtension++; + StringCopy(szBuffer, (szBufferEnd - szBuffer), szExtension); +} + //----------------------------------------------------------------------------- // Utility functions (UNICODE) only exist in the ANSI version of the library // In ANSI builds, TCHAR = char, so we don't need these functions implemented @@ -217,14 +244,13 @@ void InitializeMpqCryptography() } } -#ifdef FULL // Also register both MD5 and SHA1 hash algorithms register_hash(&md5_desc); register_hash(&sha1_desc); // Use LibTomMath as support math library for LibTomCrypt ltc_mp = ltm_desc; -#endif + // Don't do that again bMpqCryptographyInitialized = true; } @@ -339,8 +365,6 @@ DWORD GetNearestPowerOfTwo(DWORD dwFileCount) return dwPowerOfTwo; } */ - -#ifdef FULL //----------------------------------------------------------------------------- // Calculates a Jenkin's Encrypting and decrypting MPQ file data @@ -372,7 +396,6 @@ ULONGLONG HashStringJenkins(const char * szFileName) // Combine those 2 together return ((ULONGLONG)primary_hash << 0x20) | (ULONGLONG)secondary_hash; } -#endif //----------------------------------------------------------------------------- // Default flags for (attributes) and (listfile) @@ -610,7 +633,7 @@ TMPQArchive * IsValidMpqHandle(HANDLE hMpq) { TMPQArchive * ha = (TMPQArchive *)hMpq; - return (ha != NULL && ha->pHeader != NULL && ha->pHeader->dwID == ID_MPQ) ? ha : NULL; + return (ha != NULL && ha->pHeader != NULL && ha->pHeader->dwID == g_dwMpqSignature) ? ha : NULL; } TMPQFile * IsValidFileHandle(HANDLE hFile) @@ -887,6 +910,7 @@ TMPQFile * CreateWritableHandle(TMPQArchive * ha, DWORD dwFileSize) void * LoadMpqTable( TMPQArchive * ha, ULONGLONG ByteOffset, + LPBYTE pbTableHash, DWORD dwCompressedSize, DWORD dwTableSize, DWORD dwKey, @@ -925,6 +949,7 @@ void * LoadMpqTable( // On archives v 1.0, hash table and block table can go beyond EOF. // Storm.dll reads as much as possible, then fills the missing part with zeros. // Abused by Spazzler map protector which sets hash table size to 0x00100000 + // Abused by NP_Protect in MPQs v4 as well if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) { // Cut the table size @@ -941,8 +966,21 @@ void * LoadMpqTable( } } - // If everything succeeded, read the raw table form the MPQ + // If everything succeeded, read the raw table from the MPQ if(FileStream_Read(ha->pStream, &ByteOffset, pbToRead, dwBytesToRead)) + { + // Verify the MD5 of the table, if present + if(!VerifyDataBlockHash(pbToRead, dwBytesToRead, pbTableHash)) + { + nError = ERROR_FILE_CORRUPT; + } + } + else + { + nError = GetLastError(); + } + + if(nError == ERROR_SUCCESS) { // First of all, decrypt the table if(dwKey != 0) @@ -965,10 +1003,6 @@ void * LoadMpqTable( // Make sure that the table is properly byte-swapped BSWAP_ARRAY32_UNSIGNED(pbMpqTable, dwTableSize); } - else - { - nError = GetLastError(); - } // If read failed, free the table and return if(nError != ERROR_SUCCESS) @@ -1320,7 +1354,7 @@ int AllocateSectorChecksums(TMPQFile * hf, bool bLoadFromFile) RawFilePos = CalculateRawSectorOffset(hf, dwCrcOffset); // Now read the table from the MPQ - hf->SectorChksums = (DWORD *)LoadMpqTable(ha, RawFilePos, dwCompressedSize, dwCrcSize, 0, NULL); + hf->SectorChksums = (DWORD *)LoadMpqTable(ha, RawFilePos, NULL, dwCompressedSize, dwCrcSize, 0, NULL); if(hf->SectorChksums == NULL) return ERROR_NOT_ENOUGH_MEMORY; } @@ -1380,7 +1414,6 @@ int WriteSectorOffsets(TMPQFile * hf) return ERROR_SUCCESS; } - int WriteSectorChecksums(TMPQFile * hf) { TMPQArchive * ha = hf->ha; @@ -1611,10 +1644,8 @@ void FreeArchiveHandle(TMPQArchive *& ha) if(ha->pHashTable != NULL) STORM_FREE(ha->pHashTable); -#ifdef FULL if(ha->pHetTable != NULL) FreeHetTable(ha->pHetTable); -#endif STORM_FREE(ha); ha = NULL; } @@ -1674,7 +1705,7 @@ bool IsValidMD5(LPBYTE pbMd5) { LPDWORD Md5 = (LPDWORD)pbMd5; - return (Md5[0] | Md5[1] | Md5[2] | Md5[3]) ? true : false; + return ((Md5 != NULL) && (Md5[0] | Md5[1] | Md5[2] | Md5[3])) ? true : false; } bool IsValidSignature(LPBYTE pbSignature) @@ -1691,127 +1722,81 @@ bool IsValidSignature(LPBYTE pbSignature) bool VerifyDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE expected_md5) { -#ifdef FULL hash_state md5_state; BYTE md5_digest[MD5_DIGEST_SIZE]; + bool bResult = true; // Don't verify the block if the MD5 is not valid. - if(!IsValidMD5(expected_md5)) - return true; + if(IsValidMD5(expected_md5)) + { + // Calculate the MD5 of the data block + md5_init(&md5_state); + md5_process(&md5_state, (unsigned char *)pvDataBlock, cbDataBlock); + md5_done(&md5_state, md5_digest); - // Calculate the MD5 of the data block - md5_init(&md5_state); - md5_process(&md5_state, (unsigned char *)pvDataBlock, cbDataBlock); - md5_done(&md5_state, md5_digest); + // Does the MD5's match? + bResult = (memcmp(md5_digest, expected_md5, MD5_DIGEST_SIZE) == 0); + } - // Does the MD5's match? - return (memcmp(md5_digest, expected_md5, MD5_DIGEST_SIZE) == 0); -#else - assert(0); - return false; -#endif + return bResult; } void CalculateDataBlockHash(void * pvDataBlock, DWORD cbDataBlock, LPBYTE md5_hash) { -#ifdef FULL hash_state md5_state; md5_init(&md5_state); md5_process(&md5_state, (unsigned char *)pvDataBlock, cbDataBlock); md5_done(&md5_state, md5_hash); -#else - assert(0); -#endif } //----------------------------------------------------------------------------- // Swapping functions -#if !defined(PLATFORM_LITTLE_ENDIAN) -// -// Note that those functions are implemented for Mac operating system, -// as this is the only supported platform that uses big endian. -// +#ifndef STORMLIB_LITTLE_ENDIAN -#if defined(PLATFORM_MAC) // Swaps a signed 16-bit integer -int16_t SwapInt16(uint16_t data) +int16_t SwapInt16(uint16_t val) { - return (int16_t)CFSwapInt16(data); + return (val << 8) | ((val >> 8) & 0xFF); } // Swaps an unsigned 16-bit integer -uint16_t SwapUInt16(uint16_t data) -{ - return CFSwapInt16(data); -} - -// Swaps signed 32-bit integer -int32_t SwapInt32(uint32_t data) -{ - return (int32_t)CFSwapInt32(data); -} - -// Swaps an unsigned 32-bit integer -uint32_t SwapUInt32(uint32_t data) -{ - return CFSwapInt32(data); -} - -// Swaps signed 64-bit integer -int64_t SwapInt64(int64_t data) -{ - return (int64_t)CFSwapInt64(data); -} - -// Swaps an unsigned 64-bit integer -uint64_t SwapUInt64(uint64_t data) -{ - return CFSwapInt64(data); -} -#else -//! Byte swap unsigned short -uint16_t SwapUInt16( uint16_t val ) +uint16_t SwapUInt16(uint16_t val) { return (val << 8) | (val >> 8 ); } -//! Byte swap short -int16_t SwapInt16( int16_t val ) +// Swaps a signed 32-bit integer +int32_t SwapInt32(uint32_t val) { - return (val << 8) | ((val >> 8) & 0xFF); + val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF ); + return (val << 16) | ((val >> 16) & 0xFFFF); } -//! Byte swap unsigned int -uint32_t SwapUInt32( uint32_t val ) +// Swaps an unsigned 32-bit integer +uint32_t SwapUInt32(uint32_t val) { val = ((val << 8) & 0xFF00FF00 ) | ((val >> 8) & 0xFF00FF ); return (val << 16) | (val >> 16); } -//! Byte swap int -int32_t SwapInt32( int32_t val ) -{ - val = ((val << 8) & 0xFF00FF00) | ((val >> 8) & 0xFF00FF ); - return (val << 16) | ((val >> 16) & 0xFFFF); -} - -int64_t SwapInt64( int64_t val ) +// Swaps a signed 64-bit integer +int64_t SwapInt64(uint64_t val) { val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL ); val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL ); return (val << 32) | ((val >> 32) & 0xFFFFFFFFULL); } -uint64_t SwapUInt64( uint64_t val ) +// Swaps an unsigned 64-bit integer +uint64_t SwapUInt64(uint64_t val) { val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL ); val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL ); return (val << 32) | (val >> 32); } -#endif // Swaps array of unsigned 16-bit integers void ConvertUInt16Buffer(void * ptr, size_t length) @@ -1895,4 +1880,4 @@ void ConvertTMPQHeader(void *header, uint16_t version) } } -#endif // PLATFORM_LITTLE_ENDIAN +#endif // STORMLIB_LITTLE_ENDIAN diff --git a/3rdParty/StormLib/src/SBaseFileTable.cpp b/3rdParty/StormLib/src/SBaseFileTable.cpp index 4abffbeee..3d74fb1fa 100644 --- a/3rdParty/StormLib/src/SBaseFileTable.cpp +++ b/3rdParty/StormLib/src/SBaseFileTable.cpp @@ -57,19 +57,33 @@ static DWORD GetNecessaryBitCount(ULONGLONG MaxValue) } //----------------------------------------------------------------------------- -// Support functions for BIT_ARRAY +// Implementation of the TMPQBits struct -static USHORT SetBitsMask[] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF}; +struct TMPQBits +{ + static TMPQBits * Create(DWORD NumberOfBits, BYTE FillValue); + + void GetBits(unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultSize); + void SetBits(unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultSize); + + static const USHORT SetBitsMask[]; // Bit mask for each number of bits (0-8) + + DWORD NumberOfBytes; // Total number of bytes in "Elements" + DWORD NumberOfBits; // Total number of bits that are available + BYTE Elements[1]; // Array of elements (variable length) +}; + +const USHORT TMPQBits::SetBitsMask[] = {0x00, 0x01, 0x03, 0x07, 0x0F, 0x1F, 0x3F, 0x7F, 0xFF}; -static TBitArray * CreateBitArray( +TMPQBits * TMPQBits::Create( DWORD NumberOfBits, BYTE FillValue) { - TBitArray * pBitArray; - size_t nSize = sizeof(TBitArray) + (NumberOfBits + 7) / 8; + TMPQBits * pBitArray; + size_t nSize = sizeof(TMPQBits) + (NumberOfBits + 7) / 8; // Allocate the bit array - pBitArray = (TBitArray *)STORM_ALLOC(BYTE, nSize); + pBitArray = (TMPQBits *)STORM_ALLOC(BYTE, nSize); if(pBitArray != NULL) { memset(pBitArray, FillValue, nSize); @@ -80,8 +94,7 @@ static TBitArray * CreateBitArray( return pBitArray; } -void GetBits( - TBitArray * pArray, +void TMPQBits::GetBits( unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, @@ -103,7 +116,7 @@ void GetBits( assert(pbBuffer[i] == 0); #endif -#ifndef PLATFORM_LITTLE_ENDIAN +#ifndef STORMLIB_LITTLE_ENDIAN // Adjust the buffer pointer for big endian platforms pbBuffer += (nResultByteSize - 1); #endif @@ -114,14 +127,14 @@ void GetBits( // Is the current position in the Elements byte-aligned? if(nBitOffset != 0) { - BitBuffer = (unsigned char)((pArray->Elements[nBytePosition0] >> nBitOffset) | (pArray->Elements[nBytePosition1] << (0x08 - nBitOffset))); + BitBuffer = (unsigned char)((Elements[nBytePosition0] >> nBitOffset) | (Elements[nBytePosition1] << (0x08 - nBitOffset))); } else { - BitBuffer = pArray->Elements[nBytePosition0]; + BitBuffer = Elements[nBytePosition0]; } -#ifdef PLATFORM_LITTLE_ENDIAN +#ifdef STORMLIB_LITTLE_ENDIAN *pbBuffer++ = BitBuffer; #else *pbBuffer-- = BitBuffer; @@ -137,17 +150,16 @@ void GetBits( nBitLength = (nBitLength & 0x07); if(nBitLength != 0) { - *pbBuffer = (unsigned char)(pArray->Elements[nBytePosition0] >> nBitOffset); + *pbBuffer = (unsigned char)(Elements[nBytePosition0] >> nBitOffset); if(nBitLength > (8 - nBitOffset)) - *pbBuffer = (unsigned char)((pArray->Elements[nBytePosition1] << (8 - nBitOffset)) | (pArray->Elements[nBytePosition0] >> nBitOffset)); + *pbBuffer = (unsigned char)((Elements[nBytePosition1] << (8 - nBitOffset)) | (Elements[nBytePosition0] >> nBitOffset)); *pbBuffer &= (0x01 << nBitLength) - 1; } } -void SetBits( - TBitArray * pArray, +void TMPQBits::SetBits( unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, @@ -163,7 +175,7 @@ void SetBits( // Keep compiler happy for platforms where nResultByteSize is not used nResultByteSize = nResultByteSize; -#ifndef PLATFORM_LITTLE_ENDIAN +#ifndef STORMLIB_LITTLE_ENDIAN // Adjust the buffer pointer for big endian platforms pbBuffer += (nResultByteSize - 1); #endif @@ -172,7 +184,7 @@ void SetBits( while(nBitLength > 8) { // Reload the bit buffer -#ifdef PLATFORM_LITTLE_ENDIAN +#ifdef STORMLIB_LITTLE_ENDIAN OneByte = *pbBuffer++; #else OneByte = *pbBuffer--; @@ -182,7 +194,7 @@ void SetBits( AndMask = (AndMask >> 0x08) | (0x00FF << nBitOffset); // Update the byte in the array - pArray->Elements[nBytePosition] = (BYTE)((pArray->Elements[nBytePosition] & ~AndMask) | BitBuffer); + Elements[nBytePosition] = (BYTE)((Elements[nBytePosition] & ~AndMask) | BitBuffer); // Move byte positions and lengths nBytePosition++; @@ -199,7 +211,7 @@ void SetBits( AndMask = (AndMask >> 0x08) | (SetBitsMask[nBitLength] << nBitOffset); // Update the byte in the array - pArray->Elements[nBytePosition] = (BYTE)((pArray->Elements[nBytePosition] & ~AndMask) | BitBuffer); + Elements[nBytePosition] = (BYTE)((Elements[nBytePosition] & ~AndMask) | BitBuffer); // Update the next byte, if needed if(AndMask & 0xFF00) @@ -208,14 +220,56 @@ void SetBits( BitBuffer >>= 0x08; AndMask >>= 0x08; - pArray->Elements[nBytePosition] = (BYTE)((pArray->Elements[nBytePosition] & ~AndMask) | BitBuffer); + Elements[nBytePosition] = (BYTE)((Elements[nBytePosition] & ~AndMask) | BitBuffer); } } } +void GetMPQBits(TMPQBits * pBits, unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultByteSize) +{ + pBits->GetBits(nBitPosition, nBitLength, pvBuffer, nResultByteSize); +} + //----------------------------------------------------------------------------- // Support for MPQ header +static bool VerifyTablePosition64( + ULONGLONG MpqOffset, // Position of the MPQ header + ULONGLONG TableOffset, // Position of the MPQ table, relative to MPQ header + ULONGLONG TableSize, // Size of the MPQ table, in bytes + ULONGLONG FileSize) // Size of the entire file, in bytes +{ + if(TableOffset != 0) + { + // Verify overflows + if((MpqOffset + TableOffset) < MpqOffset) + return false; + if((MpqOffset + TableOffset + TableSize) < MpqOffset) + return false; + + // Verify sizes + if(TableOffset >= FileSize || TableSize >= FileSize) + return false; + if((MpqOffset + TableOffset) >= FileSize) + return false; + if((MpqOffset + TableOffset + TableSize) >= FileSize) + return false; + } + return true; +} + +static bool VerifyTableTandemPositions( + ULONGLONG MpqOffset, // Position of the MPQ header + ULONGLONG TableOffset1, // 1st table: Position, relative to MPQ header + ULONGLONG TableSize1, // 1st table: Size in bytes + ULONGLONG TableOffset2, // 2nd table: Position, relative to MPQ header + ULONGLONG TableSize2, // 2nd table: Size in bytes + ULONGLONG FileSize) // Size of the entire file, in bytes +{ + return VerifyTablePosition64(MpqOffset, TableOffset1, TableSize1, FileSize) && + VerifyTablePosition64(MpqOffset, TableOffset2, TableSize2, FileSize); +} + static ULONGLONG DetermineArchiveSize_V1( TMPQArchive * ha, TMPQHeader * pHeader, @@ -288,6 +342,45 @@ static ULONGLONG DetermineArchiveSize_V2( return (EndOfMpq - MpqOffset); } +static ULONGLONG DetermineArchiveSize_V4( + TMPQHeader * pHeader, + ULONGLONG /* MpqOffset */, + ULONGLONG /* FileSize */) +{ + ULONGLONG ArchiveSize = 0; + ULONGLONG EndOfTable; + + // This could only be called for MPQs version 4 + assert(pHeader->wFormatVersion == MPQ_FORMAT_VERSION_4); + + // Check position of BET table, if correct + if((pHeader->BetTablePos64 >> 0x20) == 0 && (pHeader->BetTableSize64 >> 0x20) == 0) + { + EndOfTable = pHeader->BetTablePos64 + pHeader->BetTableSize64; + if(EndOfTable > ArchiveSize) + ArchiveSize = EndOfTable; + } + + // Check position of HET table, if correct + if((pHeader->HetTablePos64 >> 0x20) == 0 && (pHeader->HetTableSize64 >> 0x20) == 0) + { + EndOfTable = pHeader->HetTablePos64 + pHeader->HetTableSize64; + if(EndOfTable > ArchiveSize) + ArchiveSize = EndOfTable; + } + + EndOfTable = pHeader->dwHashTablePos + pHeader->dwHashTableSize * sizeof(TMPQHash); + if(EndOfTable > ArchiveSize) + ArchiveSize = EndOfTable; + + EndOfTable = pHeader->dwBlockTablePos + pHeader->dwBlockTableSize * sizeof(TMPQBlock); + if(EndOfTable > ArchiveSize) + ArchiveSize = EndOfTable; + + // Return the calculated archive size + return ArchiveSize; +} + ULONGLONG FileOffsetFromMpqOffset(TMPQArchive * ha, ULONGLONG MpqOffset) { if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) @@ -336,24 +429,30 @@ ULONGLONG CalculateRawSectorOffset( // This function converts the MPQ header so it always looks like version 4 int ConvertMpqHeaderToFormat4( TMPQArchive * ha, - ULONGLONG MpqOffset, + ULONGLONG ByteOffset, ULONGLONG FileSize, DWORD dwFlags, - bool bIsWarcraft3Map) + MTYPE MapType) { TMPQHeader * pHeader = (TMPQHeader *)ha->HeaderData; ULONGLONG BlockTablePos64 = 0; ULONGLONG HashTablePos64 = 0; ULONGLONG BlockTableMask = (ULONGLONG)-1; - ULONGLONG ByteOffset; + ULONGLONG MaxOffset; USHORT wFormatVersion = BSWAP_INT16_UNSIGNED(pHeader->wFormatVersion); + bool bHashBlockOffsetOK = false; + bool bHetBetOffsetOK = false; int nError = ERROR_SUCCESS; // If version 1.0 is forced, then the format version is forced to be 1.0 // Reason: Storm.dll in Warcraft III ignores format version value - if((dwFlags & MPQ_OPEN_FORCE_MPQ_V1) || bIsWarcraft3Map) + if((MapType == MapTypeWarcraft3) || (dwFlags & MPQ_OPEN_FORCE_MPQ_V1)) wFormatVersion = MPQ_FORMAT_VERSION_1; + // Don't accept format 3 for Starcraft II maps + if((MapType == MapTypeStarcraft2) && (pHeader->wFormatVersion > MPQ_FORMAT_VERSION_2)) + wFormatVersion = MPQ_FORMAT_VERSION_4; + // Format-specific fixes switch(wFormatVersion) { @@ -396,16 +495,24 @@ int ConvertMpqHeaderToFormat4( // Block table position must be calculated as 32-bit value // Note: BOBA protector puts block table before the MPQ header, so it is negative - BlockTablePos64 = (ULONGLONG)((DWORD)MpqOffset + pHeader->dwBlockTablePos); + BlockTablePos64 = (ULONGLONG)((DWORD)ByteOffset + pHeader->dwBlockTablePos); BlockTableMask = 0xFFFFFFF0; // Determine the archive size on malformed MPQs if(ha->dwFlags & MPQ_FLAG_MALFORMED) { // Calculate the archive size - pHeader->ArchiveSize64 = DetermineArchiveSize_V1(ha, pHeader, MpqOffset, FileSize); + pHeader->ArchiveSize64 = DetermineArchiveSize_V1(ha, pHeader, ByteOffset, FileSize); pHeader->dwArchiveSize = (DWORD)pHeader->ArchiveSize64; } + + // EWIX_v8_7.w3x: TMPQHeader::dwBlockTableSize = 0x00319601 + // Size of TFileTable goes to ~200MB, so we artificially cut it + if(BlockTablePos64 + (pHeader->dwBlockTableSize * sizeof(TMPQBlock)) > FileSize) + { + pHeader->dwBlockTableSize = (DWORD)((FileSize - BlockTablePos64) / sizeof(TMPQBlock)); + pHeader->BlockTableSize64 = pHeader->dwBlockTableSize * sizeof(TMPQBlock); + } break; case MPQ_FORMAT_VERSION_2: @@ -447,7 +554,7 @@ int ConvertMpqHeaderToFormat4( assert(pHeader->BlockTableSize64 <= (pHeader->dwBlockTableSize * sizeof(TMPQBlock))); // Determine real archive size - pHeader->ArchiveSize64 = DetermineArchiveSize_V2(pHeader, MpqOffset, FileSize); + pHeader->ArchiveSize64 = DetermineArchiveSize_V2(pHeader, ByteOffset, FileSize); // Calculate the size of the hi-block table pHeader->HiBlockTableSize64 = pHeader->ArchiveSize64 - pHeader->HiBlockTablePos64; @@ -456,7 +563,7 @@ int ConvertMpqHeaderToFormat4( else { // Determine real archive size - pHeader->ArchiveSize64 = DetermineArchiveSize_V2(pHeader, MpqOffset, FileSize); + pHeader->ArchiveSize64 = DetermineArchiveSize_V2(pHeader, ByteOffset, FileSize); // Calculate size of the block table pHeader->BlockTableSize64 = pHeader->ArchiveSize64 - BlockTablePos64; @@ -470,7 +577,7 @@ int ConvertMpqHeaderToFormat4( } // Add the MPQ Offset - BlockTablePos64 += MpqOffset; + BlockTablePos64 += ByteOffset; break; case MPQ_FORMAT_VERSION_3: @@ -499,45 +606,45 @@ int ConvertMpqHeaderToFormat4( memset((LPBYTE)pHeader + MPQ_HEADER_SIZE_V3, 0, sizeof(TMPQHeader) - MPQ_HEADER_SIZE_V3); BlockTablePos64 = MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); HashTablePos64 = MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos); - ByteOffset = pHeader->ArchiveSize64; + MaxOffset = pHeader->ArchiveSize64; // Size of the hi-block table if(pHeader->HiBlockTablePos64) { - pHeader->HiBlockTableSize64 = ByteOffset - pHeader->HiBlockTablePos64; - ByteOffset = pHeader->HiBlockTablePos64; + pHeader->HiBlockTableSize64 = MaxOffset - pHeader->HiBlockTablePos64; + MaxOffset = pHeader->HiBlockTablePos64; } // Size of the block table if(BlockTablePos64) { - pHeader->BlockTableSize64 = ByteOffset - BlockTablePos64; - ByteOffset = BlockTablePos64; + pHeader->BlockTableSize64 = MaxOffset - BlockTablePos64; + MaxOffset = BlockTablePos64; } // Size of the hash table if(HashTablePos64) { - pHeader->HashTableSize64 = ByteOffset - HashTablePos64; - ByteOffset = HashTablePos64; + pHeader->HashTableSize64 = MaxOffset - HashTablePos64; + MaxOffset = HashTablePos64; } // Size of the BET table if(pHeader->BetTablePos64) { - pHeader->BetTableSize64 = ByteOffset - pHeader->BetTablePos64; - ByteOffset = pHeader->BetTablePos64; + pHeader->BetTableSize64 = MaxOffset - pHeader->BetTablePos64; + MaxOffset = pHeader->BetTablePos64; } // Size of the HET table if(pHeader->HetTablePos64) { - pHeader->HetTableSize64 = ByteOffset - pHeader->HetTablePos64; -// ByteOffset = pHeader->HetTablePos64; + pHeader->HetTableSize64 = MaxOffset - pHeader->HetTablePos64; +// MaxOffset = pHeader->HetTablePos64; } // Add the MPQ Offset - BlockTablePos64 += MpqOffset; + BlockTablePos64 += ByteOffset; break; case MPQ_FORMAT_VERSION_4: @@ -545,11 +652,52 @@ int ConvertMpqHeaderToFormat4( // Verify header MD5. Header MD5 is calculated from the MPQ header since the 'MPQ\x1A' // signature until the position of header MD5 at offset 0xC0 BSWAP_TMPQHEADER(pHeader, MPQ_FORMAT_VERSION_4); + + // Apparently, Starcraft II only accepts MPQ headers where the MPQ header hash matches + // If MD5 doesn't match, we ignore this offset. We also ignore it if there's no MD5 at all + if(!IsValidMD5(pHeader->MD5_MpqHeader)) + return ERROR_FAKE_MPQ_HEADER; if(!VerifyDataBlockHash(pHeader, MPQ_HEADER_SIZE_V4 - MD5_DIGEST_SIZE, pHeader->MD5_MpqHeader)) - nError = ERROR_FILE_CORRUPT; + return ERROR_FAKE_MPQ_HEADER; + + // HiBlockTable must be 0 for archives under 4GB + if((pHeader->ArchiveSize64 >> 0x20) == 0 && pHeader->HiBlockTablePos64 != 0) + return ERROR_FAKE_MPQ_HEADER; + + // Is the "HET&BET" table tandem OK? + bHetBetOffsetOK = VerifyTableTandemPositions(ByteOffset, + pHeader->HetTablePos64, pHeader->HetTableSize64, + pHeader->BetTablePos64, pHeader->BetTableSize64, + FileSize); + + // Is the "Hash&Block" table tandem OK? + bHashBlockOffsetOK = VerifyTableTandemPositions(ByteOffset, + pHeader->dwHashTablePos, pHeader->HashTableSize64, + pHeader->dwBlockTablePos, pHeader->BlockTableSize64, + FileSize); + + // At least one pair must be OK + if(bHetBetOffsetOK == false && bHashBlockOffsetOK == false) + return ERROR_FAKE_MPQ_HEADER; + + // Check for malformed MPQs + if(pHeader->wFormatVersion != MPQ_FORMAT_VERSION_4 || (ByteOffset + pHeader->ArchiveSize64) != FileSize || (ByteOffset + pHeader->HiBlockTablePos64) >= FileSize) + { + pHeader->wFormatVersion = MPQ_FORMAT_VERSION_4; + pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V4; + ha->dwFlags |= MPQ_FLAG_MALFORMED; + } + + // Recalculate archive size + if(ha->dwFlags & MPQ_FLAG_MALFORMED) + { + // Calculate the archive size + pHeader->ArchiveSize64 = DetermineArchiveSize_V4(pHeader, ByteOffset, FileSize); + pHeader->dwArchiveSize = (DWORD)pHeader->ArchiveSize64; + } // Calculate the block table position - BlockTablePos64 = MpqOffset + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); + BlockTablePos64 = ByteOffset + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); break; default: @@ -565,13 +713,13 @@ int ConvertMpqHeaderToFormat4( } // Calculate the block table position - BlockTablePos64 = MpqOffset + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); + BlockTablePos64 = ByteOffset + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); break; } // Handle case when block table is placed before the MPQ header // Used by BOBA protector - if(BlockTablePos64 < MpqOffset) + if(BlockTablePos64 < ByteOffset) ha->dwFlags |= MPQ_FLAG_MALFORMED; return nError; } @@ -1170,7 +1318,6 @@ static int SaveExtTable( return nError; } -#ifdef FULL //----------------------------------------------------------------------------- // Support for HET table @@ -1236,7 +1383,7 @@ TMPQHetTable * CreateHetTable(DWORD dwEntryCount, DWORD dwTotalCount, DWORD dwNa memset(pHetTable->pNameHashes, 0, dwTotalCount); // Allocate the bit array for file indexes - pHetTable->pBetIndexes = CreateBitArray(dwTotalCount * pHetTable->dwIndexSizeTotal, 0xFF); + pHetTable->pBetIndexes = TMPQBits::Create(dwTotalCount * pHetTable->dwIndexSizeTotal, 0xFF); if(pHetTable->pBetIndexes != NULL) { // Initialize the HET table from the source data (if given) @@ -1284,7 +1431,7 @@ static int InsertHetEntry(TMPQHetTable * pHetTable, ULONGLONG FileNameHash, DWOR pHetTable->pNameHashes[Index] = NameHash1; // Set the entry in the file index table - SetBits(pHetTable->pBetIndexes, pHetTable->dwIndexSizeTotal * Index, + pHetTable->pBetIndexes->SetBits(pHetTable->dwIndexSizeTotal * Index, pHetTable->dwIndexSize, &dwFileIndex, 4); @@ -1317,7 +1464,7 @@ static TMPQHetTable * TranslateHetTable(TMPQHetHeader * pHetHeader) if(pHetHeader->ExtHdr.dwDataSize >= (sizeof(TMPQHetHeader) - sizeof(TMPQExtHeader))) { // Verify the size of the table in the header - if(pHetHeader->dwTableSize == pHetHeader->ExtHdr.dwDataSize) + if(pHetHeader->ExtHdr.dwDataSize >= pHetHeader->dwTableSize) { // The size of the HET table must be sum of header, hash and index table size assert((sizeof(TMPQHetHeader) - sizeof(TMPQExtHeader) + pHetHeader->dwTotalCount + pHetHeader->dwIndexTableSize) == pHetHeader->dwTableSize); @@ -1390,7 +1537,6 @@ static TMPQExtHeader * TranslateHetTable(TMPQHetTable * pHetTable, ULONGLONG * p return (TMPQExtHeader *)pbLinearTable; } - static DWORD GetFileIndex_Het(TMPQArchive * ha, const char * szFileName) { TMPQHetTable * pHetTable = ha->pHetTable; @@ -1427,7 +1573,7 @@ static DWORD GetFileIndex_Het(TMPQArchive * ha, const char * szFileName) DWORD dwFileIndex = 0; // Get the file index - GetBits(pHetTable->pBetIndexes, pHetTable->dwIndexSizeTotal * Index, + pHetTable->pBetIndexes->GetBits(pHetTable->dwIndexSizeTotal * Index, pHetTable->dwIndexSize, &dwFileIndex, sizeof(DWORD)); @@ -1588,10 +1734,11 @@ static TMPQBetTable * TranslateBetTable( if(pBetHeader->ExtHdr.dwDataSize >= (sizeof(TMPQBetHeader) - sizeof(TMPQExtHeader))) { // Verify the size of the table in the header - if(pBetHeader->dwTableSize == pBetHeader->ExtHdr.dwDataSize) + if(pBetHeader->ExtHdr.dwDataSize >= pBetHeader->dwTableSize) { // The number of entries in the BET table must be the same like number of entries in the block table - assert(pBetHeader->dwEntryCount == ha->pHeader->dwBlockTableSize); + // Note: Ignored if there is no block table + //assert(pBetHeader->dwEntryCount == ha->pHeader->dwBlockTableSize); assert(pBetHeader->dwEntryCount <= ha->dwMaxFileCount); // The number of entries in the BET table must be the same like number of entries in the HET table @@ -1636,7 +1783,7 @@ static TMPQBetTable * TranslateBetTable( } // Load the bit-based file table - pBetTable->pFileTable = CreateBitArray(pBetTable->dwTableEntrySize * pBetHeader->dwEntryCount, 0); + pBetTable->pFileTable = TMPQBits::Create(pBetTable->dwTableEntrySize * pBetHeader->dwEntryCount, 0); if(pBetTable->pFileTable != NULL) { LengthInBytes = (pBetTable->pFileTable->NumberOfBits + 7) / 8; @@ -1650,7 +1797,7 @@ static TMPQBetTable * TranslateBetTable( pBetTable->dwBitCount_NameHash2 = pBetHeader->dwBitCount_NameHash2; // Create and load the array of BET hashes - pBetTable->pNameHashes = CreateBitArray(pBetTable->dwBitTotal_NameHash2 * pBetHeader->dwEntryCount, 0); + pBetTable->pNameHashes = TMPQBits::Create(pBetTable->dwBitTotal_NameHash2 * pBetHeader->dwEntryCount, 0); if(pBetTable->pNameHashes != NULL) { LengthInBytes = (pBetTable->pNameHashes->NumberOfBits + 7) / 8; @@ -1675,7 +1822,7 @@ TMPQExtHeader * TranslateBetTable( TMPQBetHeader BetHeader; TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; TFileEntry * pFileEntry; - TBitArray * pBitArray = NULL; + TMPQBits * pBitArray = NULL; LPBYTE pbLinearTable = NULL; LPBYTE pbTrgData; DWORD LengthInBytes; @@ -1695,7 +1842,7 @@ TMPQExtHeader * TranslateBetTable( pbTrgData = (LPBYTE)(pBetHeader + 1); // Save the bit-based block table - pBitArray = CreateBitArray(BetHeader.dwEntryCount * BetHeader.dwTableEntrySize, 0); + pBitArray = TMPQBits::Create(BetHeader.dwEntryCount * BetHeader.dwTableEntrySize, 0); if(pBitArray != NULL) { DWORD dwFlagIndex = 0; @@ -1710,22 +1857,22 @@ TMPQExtHeader * TranslateBetTable( // // Save the byte offset - SetBits(pBitArray, nBitOffset + BetHeader.dwBitIndex_FilePos, + pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FilePos, BetHeader.dwBitCount_FilePos, &pFileEntry->ByteOffset, 8); - SetBits(pBitArray, nBitOffset + BetHeader.dwBitIndex_FileSize, + pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FileSize, BetHeader.dwBitCount_FileSize, &pFileEntry->dwFileSize, 4); - SetBits(pBitArray, nBitOffset + BetHeader.dwBitIndex_CmpSize, + pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_CmpSize, BetHeader.dwBitCount_CmpSize, &pFileEntry->dwCmpSize, 4); // Save the flag index dwFlagIndex = GetFileFlagIndex(FlagArray, pFileEntry->dwFlags); - SetBits(pBitArray, nBitOffset + BetHeader.dwBitIndex_FlagIndex, + pBitArray->SetBits(nBitOffset + BetHeader.dwBitIndex_FlagIndex, BetHeader.dwBitCount_FlagIndex, &dwFlagIndex, 4); @@ -1750,7 +1897,7 @@ TMPQExtHeader * TranslateBetTable( } // Create bit array for name hashes - pBitArray = CreateBitArray(BetHeader.dwBitTotal_NameHash2 * BetHeader.dwEntryCount, 0); + pBitArray = TMPQBits::Create(BetHeader.dwBitTotal_NameHash2 * BetHeader.dwEntryCount, 0); if(pBitArray != NULL) { DWORD dwFileIndex = 0; @@ -1758,7 +1905,7 @@ TMPQExtHeader * TranslateBetTable( for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) { // Insert the name hash to the bit array - SetBits(pBitArray, BetHeader.dwBitTotal_NameHash2 * dwFileIndex, + pBitArray->SetBits(BetHeader.dwBitTotal_NameHash2 * dwFileIndex, BetHeader.dwBitCount_NameHash2, &pFileEntry->FileNameHash, 8); @@ -1802,7 +1949,6 @@ void FreeBetTable(TMPQBetTable * pBetTable) STORM_FREE(pBetTable); } } -#endif //----------------------------------------------------------------------------- // Support for file table @@ -1826,7 +1972,6 @@ TFileEntry * GetFileEntryLocale2(TMPQArchive * ha, const char * szFileName, LCID } } -#ifdef FULL // If we have HET table in the MPQ, try to find the file in HET table if(ha->pHetTable != NULL) { @@ -1834,7 +1979,6 @@ TFileEntry * GetFileEntryLocale2(TMPQArchive * ha, const char * szFileName, LCID if(dwFileIndex != HASH_ENTRY_FREE) return ha->pFileTable + dwFileIndex; } -#endif // Not found return NULL; @@ -1862,7 +2006,6 @@ TFileEntry * GetFileEntryExact(TMPQArchive * ha, const char * szFileName, LCID l } } -#ifdef FULL // If we have HET table in the MPQ, try to find the file in HET table if(ha->pHetTable != NULL) { @@ -1874,7 +2017,6 @@ TFileEntry * GetFileEntryExact(TMPQArchive * ha, const char * szFileName, LCID l return ha->pFileTable + dwFileIndex; } } -#endif // Not found return NULL; @@ -1901,7 +2043,6 @@ void AllocateFileName(TMPQArchive * ha, TFileEntry * pFileEntry, const char * sz strcpy(pFileEntry->szFileName, szFileName); } -#ifdef FULL // We also need to create the file name hash if(ha->pHetTable != NULL) { @@ -1910,7 +2051,6 @@ void AllocateFileName(TMPQArchive * ha, TFileEntry * pFileEntry, const char * sz pFileEntry->FileNameHash = (HashStringJenkins(szFileName) & AndMask64) | OrMask64; } -#endif } TFileEntry * AllocateFileEntry(TMPQArchive * ha, const char * szFileName, LCID lcLocale, LPDWORD PtrHashIndex) @@ -1972,14 +2112,12 @@ TFileEntry * AllocateFileEntry(TMPQArchive * ha, const char * szFileName, LCID l PtrHashIndex[0] = (DWORD)(pHash - ha->pHashTable); } -#ifdef FULL // If the archive has a HET table, just do some checks // Note: Don't bother modifying the HET table. It will be rebuilt from scratch after, anyway if(ha->pHetTable != NULL) { assert(GetFileIndex_Het(ha, szFileName) == HASH_ENTRY_FREE); } -#endif // Return the free table entry return pFreeEntry; @@ -2072,10 +2210,10 @@ int DeleteFileEntry(TMPQArchive * ha, TMPQFile * hf) return ERROR_SUCCESS; } -DWORD InvalidateInternalFile(TMPQArchive * ha, const char * szFileName, DWORD dwFlagNone, DWORD dwFlagNew) +DWORD InvalidateInternalFile(TMPQArchive * ha, const char * szFileName, DWORD dwFlagNone, DWORD dwFlagNew, DWORD dwForceAddTheFile = 0) { TMPQFile * hf = NULL; - DWORD dwFileFlags = 0; + DWORD dwFileFlags = MPQ_FILE_DEFAULT_INTERNAL; int nError = ERROR_FILE_NOT_FOUND; // Open the file from the MPQ @@ -2087,17 +2225,25 @@ DWORD InvalidateInternalFile(TMPQArchive * ha, const char * szFileName, DWORD dw // Delete the file entry nError = DeleteFileEntry(ha, hf); if(nError == ERROR_SUCCESS) - { - ha->dwFlags |= dwFlagNew; - ha->dwReservedFiles++; - } + dwForceAddTheFile = 1; - // Free the file entry + // Close the file FreeFileHandle(hf); } - // If the deletion failed, set the "none" flag - ha->dwFlags |= (nError != ERROR_SUCCESS) ? dwFlagNone : 0; + // Are we going to add the file? + if(dwForceAddTheFile) + { + ha->dwFlags |= dwFlagNew; + ha->dwReservedFiles++; + } + else + { + ha->dwFlags |= dwFlagNone; + dwFileFlags = 0; + } + + // Return the intended file flags return dwFileFlags; } @@ -2115,7 +2261,7 @@ void InvalidateInternalFiles(TMPQArchive * ha) // Invalidate the (listfile), if not done yet if((ha->dwFlags & (MPQ_FLAG_LISTFILE_NONE | MPQ_FLAG_LISTFILE_NEW)) == 0) { - ha->dwFileFlags1 = InvalidateInternalFile(ha, LISTFILE_NAME, MPQ_FLAG_LISTFILE_NONE, MPQ_FLAG_LISTFILE_NEW); + ha->dwFileFlags1 = InvalidateInternalFile(ha, LISTFILE_NAME, MPQ_FLAG_LISTFILE_NONE, MPQ_FLAG_LISTFILE_NEW, (ha->dwFlags & MPQ_FLAG_LISTFILE_FORCE)); } // Invalidate the (attributes), if not done yet @@ -2192,7 +2338,7 @@ static TMPQHash * LoadHashTable(TMPQArchive * ha) dwCmpSize = (DWORD)pHeader->HashTableSize64; // Read, decrypt and uncompress the hash table - pHashTable = (TMPQHash *)LoadMpqTable(ha, ByteOffset, dwCmpSize, dwTableSize, MPQ_KEY_HASH_TABLE, &bHashTableIsCut); + pHashTable = (TMPQHash *)LoadMpqTable(ha, ByteOffset, pHeader->MD5_HashTable, dwCmpSize, dwTableSize, g_dwHashTableKey, &bHashTableIsCut); // DumpHashTable(pHashTable, pHeader->dwHashTableSize); // If the hash table was cut, we can/have to defragment it @@ -2209,7 +2355,7 @@ static TMPQHash * LoadHashTable(TMPQArchive * ha) break; } - // Remember the size of the hash table + // Return the loaded hash table return pHashTable; } @@ -2253,7 +2399,7 @@ TMPQBlock * LoadBlockTable(TMPQArchive * ha, bool /* bDontFixEntries */) dwCmpSize = (DWORD)pHeader->BlockTableSize64; // Read, decrypt and uncompress the block table - pBlockTable = (TMPQBlock * )LoadMpqTable(ha, ByteOffset, dwCmpSize, dwTableSize, MPQ_KEY_BLOCK_TABLE, &bBlockTableIsCut); + pBlockTable = (TMPQBlock * )LoadMpqTable(ha, ByteOffset, NULL, dwCmpSize, dwTableSize, g_dwBlockTableKey, &bBlockTableIsCut); // If the block table was cut, we need to remember it if(pBlockTable != NULL && bBlockTableIsCut) @@ -2274,7 +2420,6 @@ TMPQBlock * LoadBlockTable(TMPQArchive * ha, bool /* bDontFixEntries */) return pBlockTable; } -#ifdef FULL TMPQHetTable * LoadHetTable(TMPQArchive * ha) { TMPQExtHeader * pExtTable; @@ -2282,7 +2427,7 @@ TMPQHetTable * LoadHetTable(TMPQArchive * ha) TMPQHeader * pHeader = ha->pHeader; // If the HET table position is not 0, we expect the table to be present - if(pHeader->HetTablePos64 != 0 && pHeader->HetTableSize64 != 0) + if(pHeader->HetTablePos64 && pHeader->HetTableSize64) { // Attempt to load the HET table (Hash Extended Table) pExtTable = LoadExtTable(ha, pHeader->HetTablePos64, (size_t)pHeader->HetTableSize64, HET_TABLE_SIGNATURE, MPQ_KEY_HASH_TABLE); @@ -2304,7 +2449,7 @@ TMPQBetTable * LoadBetTable(TMPQArchive * ha) TMPQHeader * pHeader = ha->pHeader; // If the BET table position is not 0, we expect the table to be present - if(pHeader->BetTablePos64 != 0 && pHeader->BetTableSize64 != 0) + if(pHeader->BetTablePos64 && pHeader->BetTableSize64) { // Attempt to load the HET table (Hash Extended Table) pExtTable = LoadExtTable(ha, pHeader->BetTablePos64, (size_t)pHeader->BetTableSize64, BET_TABLE_SIGNATURE, MPQ_KEY_BLOCK_TABLE); @@ -2319,7 +2464,6 @@ TMPQBetTable * LoadBetTable(TMPQArchive * ha) return pBetTable; } -#endif int LoadAnyHashTable(TMPQArchive * ha) { @@ -2329,12 +2473,13 @@ int LoadAnyHashTable(TMPQArchive * ha) if(pHeader->dwHashTableSize == 0 && pHeader->HetTableSize64 == 0) return CreateHashTable(ha, HASH_TABLE_SIZE_DEFAULT); -#ifdef FULL // Try to load HET table if(pHeader->HetTablePos64 != 0) ha->pHetTable = LoadHetTable(ha); -#endif + // Try to load classic hash table + // Note that we load the classic hash table even when HET table exists, + // because if the MPQ gets modified and saved, hash table must be there if(pHeader->dwHashTableSize) ha->pHashTable = LoadHashTable(ha); @@ -2419,13 +2564,12 @@ static int BuildFileTable_Classic(TMPQArchive * ha) return nError; } -#ifdef FULL static int BuildFileTable_HetBet(TMPQArchive * ha) { TMPQHetTable * pHetTable = ha->pHetTable; TMPQBetTable * pBetTable; TFileEntry * pFileEntry = ha->pFileTable; - TBitArray * pBitArray; + TMPQBits * pBitArray; DWORD dwBitPosition = 0; DWORD i; int nError = ERROR_FILE_CORRUPT; @@ -2451,7 +2595,7 @@ static int BuildFileTable_HetBet(TMPQArchive * ha) if(pHetTable->pNameHashes[i] != HET_ENTRY_FREE) { // Load the index to the BET table - GetBits(pHetTable->pBetIndexes, pHetTable->dwIndexSizeTotal * i, + pHetTable->pBetIndexes->GetBits(pHetTable->dwIndexSizeTotal * i, pHetTable->dwIndexSize, &dwFileIndex, 4); @@ -2462,7 +2606,7 @@ static int BuildFileTable_HetBet(TMPQArchive * ha) ULONGLONG NameHash2 = 0; // Load the BET hash - GetBits(pBetTable->pNameHashes, pBetTable->dwBitTotal_NameHash2 * dwFileIndex, + pBetTable->pNameHashes->GetBits(pBetTable->dwBitTotal_NameHash2 * dwFileIndex, pBetTable->dwBitCount_NameHash2, &NameHash2, 8); @@ -2482,19 +2626,19 @@ static int BuildFileTable_HetBet(TMPQArchive * ha) DWORD dwFlagIndex = 0; // Read the file position - GetBits(pBitArray, dwBitPosition + pBetTable->dwBitIndex_FilePos, + pBitArray->GetBits(dwBitPosition + pBetTable->dwBitIndex_FilePos, pBetTable->dwBitCount_FilePos, &pFileEntry->ByteOffset, 8); // Read the file size - GetBits(pBitArray, dwBitPosition + pBetTable->dwBitIndex_FileSize, + pBitArray->GetBits(dwBitPosition + pBetTable->dwBitIndex_FileSize, pBetTable->dwBitCount_FileSize, &pFileEntry->dwFileSize, 4); // Read the compressed size - GetBits(pBitArray, dwBitPosition + pBetTable->dwBitIndex_CmpSize, + pBitArray->GetBits(dwBitPosition + pBetTable->dwBitIndex_CmpSize, pBetTable->dwBitCount_CmpSize, &pFileEntry->dwCmpSize, 4); @@ -2503,7 +2647,7 @@ static int BuildFileTable_HetBet(TMPQArchive * ha) // Read the flag index if(pBetTable->dwFlagCount != 0) { - GetBits(pBitArray, dwBitPosition + pBetTable->dwBitIndex_FlagIndex, + pBitArray->GetBits(dwBitPosition + pBetTable->dwBitIndex_FlagIndex, pBetTable->dwBitCount_FlagIndex, &dwFlagIndex, 4); @@ -2530,7 +2674,6 @@ static int BuildFileTable_HetBet(TMPQArchive * ha) return nError; } -#endif int BuildFileTable(TMPQArchive * ha) { @@ -2554,7 +2697,6 @@ int BuildFileTable(TMPQArchive * ha) memset(ha->pFileTable, 0, dwFileTableSize * sizeof(TFileEntry)); ha->dwFileTableSize = dwFileTableSize; -#ifdef FULL // If we have HET table, we load file table from the BET table // Note: If BET table is corrupt or missing, we set the archive as read only if(ha->pHetTable != NULL) @@ -2564,7 +2706,6 @@ int BuildFileTable(TMPQArchive * ha) else bFileTableCreated = true; } -#endif // If we have hash table, we load the file table from the block table // Note: If block table is corrupt or missing, we set the archive as read only @@ -2600,7 +2741,6 @@ void UpdateBlockTableSize(TMPQArchive * ha) } */ -#ifdef FULL // Defragment the file table so it does not contain any gaps int DefragmentFileTable(TMPQArchive * ha) { @@ -2959,4 +3099,3 @@ int SaveMPQTables(TMPQArchive * ha) STORM_FREE(pHiBlockTable); return nError; } -#endif diff --git a/3rdParty/StormLib/src/SBaseSubTypes.cpp b/3rdParty/StormLib/src/SBaseSubTypes.cpp index 47c205e47..afa7311ef 100644 --- a/3rdParty/StormLib/src/SBaseSubTypes.cpp +++ b/3rdParty/StormLib/src/SBaseSubTypes.cpp @@ -21,24 +21,24 @@ typedef struct _TSQPHeader { // The ID_MPQ ('MPQ\x1A') signature - DWORD dwID; + DWORD dwID; // Size of the archive header - DWORD dwHeaderSize; + DWORD dwHeaderSize; // 32-bit size of MPQ archive DWORD dwArchiveSize; // Offset to the beginning of the hash table, relative to the beginning of the archive. DWORD dwHashTablePos; - + // Offset to the beginning of the block table, relative to the beginning of the archive. DWORD dwBlockTablePos; - + // Number of entries in the hash table. Must be a power of two, and must be less than 2^16 for // the original MoPaQ format, or less than 2^20 for the Burning Crusade format. DWORD dwHashTableSize; - + // Number of entries in the block table DWORD dwBlockTableSize; @@ -66,7 +66,7 @@ typedef struct _TSQPHash // The hash of the file path, using method A. DWORD dwName1; - + // The hash of the file path, using method B. DWORD dwName2; @@ -76,16 +76,16 @@ typedef struct _TSQPBlock { // Offset of the beginning of the file, relative to the beginning of the archive. DWORD dwFilePos; - + // Flags for the file. See MPQ_FILE_XXXX constants - DWORD dwFlags; + DWORD dwFlags; // Compressed file size DWORD dwCSize; - + // Uncompressed file size - DWORD dwFSize; - + DWORD dwFSize; + } TSQPBlock; //----------------------------------------------------------------------------- @@ -131,7 +131,7 @@ int ConvertSqpHeaderToFormat4( Header.wSectorSize = BSWAP_INT16_UNSIGNED(pSqpHeader->wSectorSize); // Verify the SQP header - if(Header.dwID == ID_MPQ && Header.dwHeaderSize == sizeof(TSQPHeader) && Header.dwArchiveSize == FileSize) + if(Header.dwID == g_dwMpqSignature && Header.dwHeaderSize == sizeof(TSQPHeader) && Header.dwArchiveSize == FileSize) { // Check for fixed values of version and sector size if(Header.wFormatVersion == MPQ_FORMAT_VERSION_1 && Header.wSectorSize == 3) @@ -143,7 +143,7 @@ int ConvertSqpHeaderToFormat4( // Copy the converted MPQ header back memcpy(ha->HeaderData, &Header, sizeof(TMPQHeader)); - + // Mark this file as SQP file ha->pfnHashString = HashStringSlash; ha->dwFlags |= MPQ_FLAG_READ_ONLY; @@ -292,13 +292,13 @@ typedef struct _TMPKHeader { // The ID_MPK ('MPK\x1A') signature DWORD dwID; - + // Contains '2000' DWORD dwVersion; - + // 32-bit size of the archive DWORD dwArchiveSize; - + // Size of the archive header DWORD dwHeaderSize; @@ -315,7 +315,7 @@ typedef struct _TMPKHash { // The hash of the file path, using method A. DWORD dwName1; - + // The hash of the file path, using method B. DWORD dwName2; @@ -334,7 +334,7 @@ typedef struct _TMPKHash typedef struct _TMPKBlock { - DWORD dwFlags; // 0x1121 - Compressed , 0x1120 - Not compressed + DWORD dwFlags; // 0x1121 - Compressed , 0x1120 - Not compressed DWORD dwFilePos; // Offset of the beginning of the file, relative to the beginning of the archive. DWORD dwFSize; // Uncompressed file size DWORD dwCSize; // Compressed file size @@ -414,7 +414,7 @@ int ConvertMpkHeaderToFormat4( if(Header.dwID == ID_MPK && Header.dwHeaderSize == sizeof(TMPKHeader) && Header.dwArchiveSize == (DWORD)FileSize) { // The header ID must be ID_MPQ - Header.dwID = ID_MPQ; + Header.dwID = g_dwMpqSignature; Header.wFormatVersion = MPQ_FORMAT_VERSION_1; Header.wSectorSize = 3; diff --git a/3rdParty/StormLib/src/SCompression.cpp b/3rdParty/StormLib/src/SCompression.cpp index 8f15b9314..93f80e104 100644 --- a/3rdParty/StormLib/src/SCompression.cpp +++ b/3rdParty/StormLib/src/SCompression.cpp @@ -1,1166 +1,1145 @@ -/*****************************************************************************/ -/* SCompression.cpp Copyright (c) Ladislav Zezula 2003 */ -/*---------------------------------------------------------------------------*/ -/* This module serves as a bridge between StormLib code and (de)compression */ -/* functions. All (de)compression calls go (and should only go) through this */ -/* module. No system headers should be included in this module to prevent */ -/* compile-time problems. */ -/*---------------------------------------------------------------------------*/ -/* Date Ver Who Comment */ -/* -------- ---- --- ------- */ -/* 01.04.03 1.00 Lad The first version of SCompression.cpp */ -/* 19.11.03 1.01 Dan Big endian handling */ -/*****************************************************************************/ - -#define __STORMLIB_SELF__ -#include "StormLib.h" -#include "StormCommon.h" - -//----------------------------------------------------------------------------- -// Local structures - -// Information about the input and output buffers for pklib -typedef struct -{ - unsigned char * pbInBuff; // Pointer to input data buffer - unsigned char * pbInBuffEnd; // End of the input buffer - unsigned char * pbOutBuff; // Pointer to output data buffer - unsigned char * pbOutBuffEnd; // Pointer to output data buffer -} TDataInfo; - -// Prototype of the compression function -// Function doesn't return an error. A success means that the size of compressed buffer -// is lower than size of uncompressed buffer. -typedef void (*COMPRESS)( - void * pvOutBuffer, // [out] Pointer to the buffer where the compressed data will be stored - int * pcbOutBuffer, // [in] Pointer to length of the buffer pointed by pvOutBuffer - void * pvInBuffer, // [in] Pointer to the buffer with data to compress - int cbInBuffer, // [in] Length of the buffer pointer by pvInBuffer - int * pCmpType, // [in] Compression-method specific value. ADPCM Setups this for the following Huffman compression - int nCmpLevel); // [in] Compression specific value. ADPCM uses this. Should be set to zero. - -// Prototype of the decompression function -// Returns 1 if success, 0 if failure -typedef int (*DECOMPRESS)( - void * pvOutBuffer, // [out] Pointer to the buffer where to store decompressed data - int * pcbOutBuffer, // [in] Pointer to total size of the buffer pointed by pvOutBuffer - // [out] Contains length of the decompressed data - void * pvInBuffer, // [in] Pointer to data to be decompressed - int cbInBuffer); // [in] Length of the data to be decompressed - -// Table of compression functions -typedef struct -{ - unsigned long uMask; // Compression mask - COMPRESS Compress; // Compression function -} TCompressTable; - -// Table of decompression functions -typedef struct -{ - unsigned long uMask; // Decompression bit - DECOMPRESS Decompress; // Decompression function -} TDecompressTable; - - -#ifdef FULL -/*****************************************************************************/ -/* */ -/* Support for Huffman compression (0x01) */ -/* */ -/*****************************************************************************/ - -void Compress_huff(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) -{ - THuffmannTree ht(true); - TOutputStream os(pvOutBuffer, *pcbOutBuffer); - - STORMLIB_UNUSED(nCmpLevel); - *pcbOutBuffer = ht.Compress(&os, pvInBuffer, cbInBuffer, *pCmpType); -} - -int Decompress_huff(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - THuffmannTree ht(false); - TInputStream is(pvInBuffer, cbInBuffer); - - *pcbOutBuffer = ht.Decompress(pvOutBuffer, *pcbOutBuffer, &is); - return (*pcbOutBuffer == 0) ? 0 : 1; -} - -/******************************************************************************/ -/* */ -/* Support for ZLIB compression (0x02) */ -/* */ -/******************************************************************************/ - -void Compress_ZLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) -{ - z_stream z; // Stream information for zlib - int windowBits; - int nResult; - - // Keep compilers happy - STORMLIB_UNUSED(pCmpType); - STORMLIB_UNUSED(nCmpLevel); - - // Fill the stream structure for zlib - z.next_in = (Bytef *)pvInBuffer; - z.avail_in = (uInt)cbInBuffer; - z.total_in = cbInBuffer; - z.next_out = (Bytef *)pvOutBuffer; - z.avail_out = *pcbOutBuffer; - z.total_out = 0; - z.zalloc = NULL; - z.zfree = NULL; - - // Determine the proper window bits (WoW.exe build 12694) - if(cbInBuffer <= 0x100) - windowBits = 8; - else if(cbInBuffer <= 0x200) - windowBits = 9; - else if(cbInBuffer <= 0x400) - windowBits = 10; - else if(cbInBuffer <= 0x800) - windowBits = 11; - else if(cbInBuffer <= 0x1000) - windowBits = 12; - else if(cbInBuffer <= 0x2000) - windowBits = 13; - else if(cbInBuffer <= 0x4000) - windowBits = 14; - else - windowBits = 15; - - // Initialize the compression. - // Storm.dll uses zlib version 1.1.3 - // Wow.exe uses zlib version 1.2.3 - nResult = deflateInit2(&z, - 6, // Compression level used by WoW MPQs - Z_DEFLATED, - windowBits, - 8, - Z_DEFAULT_STRATEGY); - if(nResult == Z_OK) - { - // Call zlib to compress the data - nResult = deflate(&z, Z_FINISH); - - if(nResult == Z_OK || nResult == Z_STREAM_END) - *pcbOutBuffer = z.total_out; - - deflateEnd(&z); - } -} - -int Decompress_ZLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - z_stream z; // Stream information for zlib - int nResult; - - // Fill the stream structure for zlib - z.next_in = (Bytef *)pvInBuffer; - z.avail_in = (uInt)cbInBuffer; - z.total_in = cbInBuffer; - z.next_out = (Bytef *)pvOutBuffer; - z.avail_out = *pcbOutBuffer; - z.total_out = 0; - z.zalloc = NULL; - z.zfree = NULL; - - // Initialize the decompression structure. Storm.dll uses zlib version 1.1.3 - if((nResult = inflateInit(&z)) == 0) - { - // Call zlib to decompress the data - nResult = inflate(&z, Z_FINISH); - *pcbOutBuffer = z.total_out; - inflateEnd(&z); - } - return nResult; -} -#endif - -/******************************************************************************/ -/* */ -/* Support functions for PKWARE Data Compression Library compression (0x08) */ -/* */ -/******************************************************************************/ - -// Function loads data from the input buffer. Used by Pklib's "implode" -// and "explode" function as user-defined callback -// Returns number of bytes loaded -// -// char * buf - Pointer to a buffer where to store loaded data -// unsigned int * size - Max. number of bytes to read -// void * param - Custom pointer, parameter of implode/explode - -static unsigned int ReadInputData(char * buf, unsigned int * size, void * param) -{ - TDataInfo * pInfo = (TDataInfo *)param; - unsigned int nMaxAvail = (unsigned int)(pInfo->pbInBuffEnd - pInfo->pbInBuff); - unsigned int nToRead = *size; - - // Check the case when not enough data available - if(nToRead > nMaxAvail) - nToRead = nMaxAvail; - - // Load data and increment offsets - memcpy(buf, pInfo->pbInBuff, nToRead); - pInfo->pbInBuff += nToRead; - assert(pInfo->pbInBuff <= pInfo->pbInBuffEnd); - return nToRead; -} - -// Function for store output data. Used by Pklib's "implode" and "explode" -// as user-defined callback -// -// char * buf - Pointer to data to be written -// unsigned int * size - Number of bytes to write -// void * param - Custom pointer, parameter of implode/explode - -static void WriteOutputData(char * buf, unsigned int * size, void * param) -{ - TDataInfo * pInfo = (TDataInfo *)param; - unsigned int nMaxWrite = (unsigned int)(pInfo->pbOutBuffEnd - pInfo->pbOutBuff); - unsigned int nToWrite = *size; - - // Check the case when not enough space in the output buffer - if(nToWrite > nMaxWrite) - nToWrite = nMaxWrite; - - // Write output data and increments offsets - memcpy(pInfo->pbOutBuff, buf, nToWrite); - pInfo->pbOutBuff += nToWrite; - assert(pInfo->pbOutBuff <= pInfo->pbOutBuffEnd); -} - -static void Compress_PKLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) -{ - TDataInfo Info; // Data information - char * work_buf = STORM_ALLOC(char, CMP_BUFFER_SIZE);// Pklib's work buffer - unsigned int dict_size; // Dictionary size - unsigned int ctype = CMP_BINARY; // Compression type - - // Keep compilers happy - STORMLIB_UNUSED(pCmpType); - STORMLIB_UNUSED(nCmpLevel); - - // Handle no-memory condition - if(work_buf != NULL) - { - // Fill data information structure - memset(work_buf, 0, CMP_BUFFER_SIZE); - Info.pbInBuff = (unsigned char *)pvInBuffer; - Info.pbInBuffEnd = (unsigned char *)pvInBuffer + cbInBuffer; - Info.pbOutBuff = (unsigned char *)pvOutBuffer; - Info.pbOutBuffEnd = (unsigned char *)pvOutBuffer + *pcbOutBuffer; - - // - // Set the dictionary size - // - // Diablo I uses fixed dictionary size of CMP_IMPLODE_DICT_SIZE3 - // Starcraft I uses the variable dictionary size based on algorithm below - // - - if (cbInBuffer < 0x600) - dict_size = CMP_IMPLODE_DICT_SIZE1; - else if(0x600 <= cbInBuffer && cbInBuffer < 0xC00) - dict_size = CMP_IMPLODE_DICT_SIZE2; - else - dict_size = CMP_IMPLODE_DICT_SIZE3; - - // Do the compression - if(implode(ReadInputData, WriteOutputData, work_buf, &Info, &ctype, &dict_size) == CMP_NO_ERROR) - *pcbOutBuffer = (int)(Info.pbOutBuff - (unsigned char *)pvOutBuffer); - - STORM_FREE(work_buf); - } -} - -static int Decompress_PKLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - TDataInfo Info; // Data information - char * work_buf = STORM_ALLOC(char, EXP_BUFFER_SIZE);// Pklib's work buffer - - // Handle no-memory condition - if(work_buf == NULL) - return 0; - - // Fill data information structure - memset(work_buf, 0, EXP_BUFFER_SIZE); - Info.pbInBuff = (unsigned char *)pvInBuffer; - Info.pbInBuffEnd = (unsigned char *)pvInBuffer + cbInBuffer; - Info.pbOutBuff = (unsigned char *)pvOutBuffer; - Info.pbOutBuffEnd = (unsigned char *)pvOutBuffer + *pcbOutBuffer; - - // Do the decompression - explode(ReadInputData, WriteOutputData, work_buf, &Info); - - // If PKLIB is unable to decompress the data, return 0; - if(Info.pbOutBuff == pvOutBuffer) - { - STORM_FREE(work_buf); - return 0; - } - - // Give away the number of decompressed bytes - *pcbOutBuffer = (int)(Info.pbOutBuff - (unsigned char *)pvOutBuffer); - STORM_FREE(work_buf); - return 1; -} - -#ifdef FULL -/******************************************************************************/ -/* */ -/* Support for Bzip2 compression (0x10) */ -/* */ -/******************************************************************************/ - -static void Compress_BZIP2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) -{ - bz_stream strm; - int blockSize100k = 9; - int workFactor = 30; - int bzError; - - // Keep compilers happy - STORMLIB_UNUSED(pCmpType); - STORMLIB_UNUSED(nCmpLevel); - - // Initialize the BZIP2 compression - strm.bzalloc = NULL; - strm.bzfree = NULL; - strm.opaque = NULL; - - // Blizzard uses 9 as blockSize100k, (0x30 as workFactor) - // Last checked on Starcraft II - if(BZ2_bzCompressInit(&strm, blockSize100k, 0, workFactor) == BZ_OK) - { - strm.next_in = (char *)pvInBuffer; - strm.avail_in = cbInBuffer; - strm.next_out = (char *)pvOutBuffer; - strm.avail_out = *pcbOutBuffer; - - // Perform the compression - for(;;) - { - bzError = BZ2_bzCompress(&strm, (strm.avail_in != 0) ? BZ_RUN : BZ_FINISH); - if(bzError == BZ_STREAM_END || bzError < 0) - break; - } - - // Put the stream into idle state - BZ2_bzCompressEnd(&strm); - - if(bzError > 0) - *pcbOutBuffer = strm.total_out_lo32; - } -} - -static int Decompress_BZIP2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - bz_stream strm; - int nResult = BZ_OK; - - // Initialize the BZIP2 decompression - strm.bzalloc = NULL; - strm.bzfree = NULL; - strm.opaque = NULL; - - // Initialize decompression - if(BZ2_bzDecompressInit(&strm, 0, 0) == BZ_OK) - { - strm.next_in = (char *)pvInBuffer; - strm.avail_in = cbInBuffer; - strm.next_out = (char *)pvOutBuffer; - strm.avail_out = *pcbOutBuffer; - - // Perform the decompression - while(nResult != BZ_STREAM_END) - { - nResult = BZ2_bzDecompress(&strm); - - // If any error there, break the loop - if(nResult < BZ_OK) - break; - } - - // Put the stream into idle state - BZ2_bzDecompressEnd(&strm); - - // If all succeeded, set the number of output bytes - if(nResult >= BZ_OK) - { - *pcbOutBuffer = strm.total_out_lo32; - return 1; - } - } - - // Something failed, so set number of output bytes to zero - *pcbOutBuffer = 0; - return 1; -} - -/******************************************************************************/ -/* */ -/* Support functions for LZMA compression (0x12) */ -/* */ -/******************************************************************************/ - -#define LZMA_HEADER_SIZE (1 + LZMA_PROPS_SIZE + 8) - -static SRes LZMA_Callback_Progress(void * /* p */, UInt64 /* inSize */, UInt64 /* outSize */) -{ - return SZ_OK; -} - -static void * LZMA_Callback_Alloc(void *p, size_t size) -{ - p = p; - return STORM_ALLOC(BYTE, size); -} - -/* address can be 0 */ -static void LZMA_Callback_Free(void *p, void *address) -{ - p = p; - if(address != NULL) - STORM_FREE(address); -} - -// -// Note: So far, I haven't seen any files compressed by LZMA. -// This code haven't been verified against code ripped from Starcraft II Beta, -// but we know that Starcraft LZMA decompression code is able to decompress -// the data compressed by StormLib. -// - -static void Compress_LZMA(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) -{ - ICompressProgress Progress; - CLzmaEncProps props; - ISzAlloc SzAlloc; - Byte * pbOutBuffer = (Byte *)pvOutBuffer; - Byte * destBuffer; - SizeT destLen = *pcbOutBuffer; - SizeT srcLen = cbInBuffer; - Byte encodedProps[LZMA_PROPS_SIZE]; - size_t encodedPropsSize = LZMA_PROPS_SIZE; - SRes nResult; - - // Keep compilers happy - STORMLIB_UNUSED(pCmpType); - STORMLIB_UNUSED(nCmpLevel); - - // Fill the callbacks in structures - Progress.Progress = LZMA_Callback_Progress; - SzAlloc.Alloc = LZMA_Callback_Alloc; - SzAlloc.Free = LZMA_Callback_Free; - - // Initialize properties - LzmaEncProps_Init(&props); - - // Perform compression - destBuffer = (Byte *)pvOutBuffer + LZMA_HEADER_SIZE; - destLen = *pcbOutBuffer - LZMA_HEADER_SIZE; - nResult = LzmaEncode(destBuffer, - &destLen, - (Byte *)pvInBuffer, - srcLen, - &props, - encodedProps, - &encodedPropsSize, - 0, - &Progress, - &SzAlloc, - &SzAlloc); - if(nResult != SZ_OK) - return; - - // If we failed to compress the data - if(destLen >= (SizeT)(*pcbOutBuffer - LZMA_HEADER_SIZE)) - return; - - // Write "useFilter" variable. Blizzard MPQ must not use filter. - *pbOutBuffer++ = 0; - - // Copy the encoded properties to the output buffer - memcpy(pvOutBuffer, encodedProps, encodedPropsSize); - pbOutBuffer += encodedPropsSize; - - // Copy the size of the data - *pbOutBuffer++ = (unsigned char)(srcLen >> 0x00); - *pbOutBuffer++ = (unsigned char)(srcLen >> 0x08); - *pbOutBuffer++ = (unsigned char)(srcLen >> 0x10); - *pbOutBuffer++ = (unsigned char)(srcLen >> 0x18); - *pbOutBuffer++ = 0; - *pbOutBuffer++ = 0; - *pbOutBuffer++ = 0; - *pbOutBuffer++ = 0; - - // Give the size of the data to the caller - *pcbOutBuffer = (unsigned int)(destLen + LZMA_HEADER_SIZE); -} - -static int Decompress_LZMA(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - ELzmaStatus LzmaStatus; - ISzAlloc SzAlloc; - Byte * destBuffer = (Byte *)pvOutBuffer; - Byte * srcBuffer = (Byte *)pvInBuffer; - SizeT destLen = *pcbOutBuffer; - SizeT srcLen = cbInBuffer; - SRes nResult; - - // There must be at least 0x0E bytes in the buffer - if(srcLen <= LZMA_HEADER_SIZE) - return 0; - - // We only accept blocks that have no filter used - if(*srcBuffer != 0) - return 0; - - // Fill the callbacks in structures - SzAlloc.Alloc = LZMA_Callback_Alloc; - SzAlloc.Free = LZMA_Callback_Free; - - // Perform compression - srcLen = cbInBuffer - LZMA_HEADER_SIZE; - nResult = LzmaDecode(destBuffer, - &destLen, - srcBuffer + LZMA_HEADER_SIZE, - &srcLen, - srcBuffer + 1, - LZMA_PROPS_SIZE, - LZMA_FINISH_END, - &LzmaStatus, - &SzAlloc); - if(nResult != SZ_OK) - return 0; - - *pcbOutBuffer = (unsigned int)destLen; - return 1; -} - -static int Decompress_LZMA_MPK(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - ELzmaStatus LzmaStatus; - ISzAlloc SzAlloc; - Byte * destBuffer = (Byte *)pvOutBuffer; - Byte * srcBuffer = (Byte *)pvInBuffer; - SizeT destLen = *pcbOutBuffer; - SizeT srcLen = cbInBuffer; - SRes nResult; - BYTE LZMA_Props[] = {0x5D, 0x00, 0x00, 0x00, 0x01}; - - // There must be at least 0x0E bytes in the buffer - if(srcLen <= sizeof(LZMA_Props)) - return 0; - - // Verify the props header - if(memcmp(pvInBuffer, LZMA_Props, sizeof(LZMA_Props))) - return 0; - - // Fill the callbacks in structures - SzAlloc.Alloc = LZMA_Callback_Alloc; - SzAlloc.Free = LZMA_Callback_Free; - - // Perform compression - srcLen = cbInBuffer - sizeof(LZMA_Props); - nResult = LzmaDecode(destBuffer, - &destLen, - srcBuffer + sizeof(LZMA_Props), - &srcLen, - srcBuffer, - sizeof(LZMA_Props), - LZMA_FINISH_END, - &LzmaStatus, - &SzAlloc); - if(nResult != SZ_OK) - return 0; - - *pcbOutBuffer = (unsigned int)destLen; - return 1; -} - -/******************************************************************************/ -/* */ -/* Support functions for SPARSE compression (0x20) */ -/* */ -/******************************************************************************/ - -void Compress_SPARSE(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) -{ - // Keep compilers happy - STORMLIB_UNUSED(pCmpType); - STORMLIB_UNUSED(nCmpLevel); - - CompressSparse(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer); -} - -int Decompress_SPARSE(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - return DecompressSparse(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer); -} - -/******************************************************************************/ -/* */ -/* Support for ADPCM mono compression (0x40) */ -/* */ -/******************************************************************************/ - -static void Compress_ADPCM_mono(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) -{ - // Prepare the compression level for Huffmann compression, - // which will be called as next step - if(0 < nCmpLevel && nCmpLevel <= 2) - { - nCmpLevel = 4; - *pCmpType = 6; - } - else if(nCmpLevel == 3) - { - nCmpLevel = 6; - *pCmpType = 8; - } - else - { - nCmpLevel = 5; - *pCmpType = 7; - } - *pcbOutBuffer = CompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 1, nCmpLevel); -} - -static int Decompress_ADPCM_mono(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - *pcbOutBuffer = DecompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 1); - return 1; -} - -/******************************************************************************/ -/* */ -/* Support for ADPCM stereo compression (0x80) */ -/* */ -/******************************************************************************/ - -static void Compress_ADPCM_stereo(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) -{ - // Prepare the compression level for Huffmann compression, - // which will be called as next step - if(0 < nCmpLevel && nCmpLevel <= 2) - { - nCmpLevel = 4; - *pCmpType = 6; - } - else if(nCmpLevel == 3) - { - nCmpLevel = 6; - *pCmpType = 8; - } - else - { - nCmpLevel = 5; - *pCmpType = 7; - } - *pcbOutBuffer = CompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 2, nCmpLevel); -} - -static int Decompress_ADPCM_stereo(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - *pcbOutBuffer = DecompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 2); - return 1; -} -#endif - -/*****************************************************************************/ -/* */ -/* SCompImplode */ -/* */ -/*****************************************************************************/ - -int STORMAPI SCompImplode(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - int cbOutBuffer; - - // Check for valid parameters - if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer) - { - SetLastError(ERROR_INVALID_PARAMETER); - return 0; - } - - // Perform the compression - cbOutBuffer = *pcbOutBuffer; - Compress_PKLIB(pvOutBuffer, &cbOutBuffer, pvInBuffer, cbInBuffer, NULL, 0); - - // If the compression was unsuccessful, copy the data as-is - if(cbOutBuffer >= *pcbOutBuffer) - { - memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); - cbOutBuffer = *pcbOutBuffer; - } - - *pcbOutBuffer = cbOutBuffer; - return 1; -} - -/*****************************************************************************/ -/* */ -/* SCompExplode */ -/* */ -/*****************************************************************************/ - -int STORMAPI SCompExplode(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - int cbOutBuffer; - - // Check for valid parameters - if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer) - { - SetLastError(ERROR_INVALID_PARAMETER); - return 0; - } - - // If the input length is the same as output length, do nothing. - cbOutBuffer = *pcbOutBuffer; - if(cbInBuffer == cbOutBuffer) - { - // If the buffers are equal, don't copy anything. - if(pvInBuffer == pvOutBuffer) - return 1; - - memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); - return 1; - } - - // Perform decompression - if(!Decompress_PKLIB(pvOutBuffer, &cbOutBuffer, pvInBuffer, cbInBuffer)) - { - SetLastError(ERROR_FILE_CORRUPT); - return 0; - } - - *pcbOutBuffer = cbOutBuffer; - return 1; -} - -/*****************************************************************************/ -/* */ -/* SCompCompress */ -/* */ -/*****************************************************************************/ - -// This table contains compress functions which can be applied to -// uncompressed data. Each bit means the corresponding -// compression method/function must be applied. -// -// WAVes compression Data compression -// ------------------ ------------------- -// 1st sector - 0x08 0x08 (D, HF, W2, SC, D2) -// Next sectors - 0x81 0x02 (W3) - -static TCompressTable cmp_table[] = -{ -#ifdef FULL - {MPQ_COMPRESSION_SPARSE, Compress_SPARSE}, // Sparse compression - {MPQ_COMPRESSION_ADPCM_MONO, Compress_ADPCM_mono}, // IMA ADPCM mono compression - {MPQ_COMPRESSION_ADPCM_STEREO, Compress_ADPCM_stereo}, // IMA ADPCM stereo compression - {MPQ_COMPRESSION_HUFFMANN, Compress_huff}, // Huffmann compression - {MPQ_COMPRESSION_ZLIB, Compress_ZLIB}, // Compression with the "zlib" library -#endif - {MPQ_COMPRESSION_PKWARE, Compress_PKLIB}, // Compression with Pkware DCL -#ifdef FULL - {MPQ_COMPRESSION_BZIP2, Compress_BZIP2} // Compression Bzip2 library -#endif -}; - -int STORMAPI SCompCompress(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, unsigned uCompressionMask, int nCmpType, int nCmpLevel) -{ - COMPRESS CompressFuncArray[0x10]; // Array of compression functions, applied sequentially - unsigned char CompressByte[0x10]; // CompressByte for each method in the CompressFuncArray array - unsigned char * pbWorkBuffer = NULL; // Temporary storage for decompressed data - unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer; - unsigned char * pbOutput = (unsigned char *)pvOutBuffer;// Current output buffer - unsigned char * pbInput = (unsigned char *)pvInBuffer; // Current input buffer - int nCompressCount = 0; - int nCompressIndex = 0; - int nAtLeastOneCompressionDone = 0; - int cbOutBuffer = 0; - int cbInLength = cbInBuffer; - int nResult = 1; - - // Check for valid parameters - if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer) - { - SetLastError(ERROR_INVALID_PARAMETER); - return 0; - } - - // Zero input length brings zero output length - if(cbInBuffer == 0) - { - *pcbOutBuffer = 0; - return true; - } - - // Setup the compression function array - if(uCompressionMask == MPQ_COMPRESSION_LZMA) - { -#ifdef FULL - CompressFuncArray[0] = Compress_LZMA; - CompressByte[0] = (char)uCompressionMask; - nCompressCount = 1; -#else - assert(0); -#endif - } - else - { - // Fill the compressions array - for(size_t i = 0; i < (sizeof(cmp_table) / sizeof(TCompressTable)); i++) - { - // If the mask agrees, insert the compression function to the array - if(uCompressionMask & cmp_table[i].uMask) - { - CompressFuncArray[nCompressCount] = cmp_table[i].Compress; - CompressByte[nCompressCount] = (unsigned char)cmp_table[i].uMask; - uCompressionMask &= ~cmp_table[i].uMask; - nCompressCount++; - } - } - - // If at least one of the compressions remaing unknown, return an error - if(uCompressionMask != 0) - { - SetLastError(ERROR_NOT_SUPPORTED); - return 0; - } - } - - // If there is at least one compression, do it - if(nCompressCount > 0) - { - // If we need to do more than 1 compression, allocate intermediate buffer - if(nCompressCount > 1) - { - pbWorkBuffer = STORM_ALLOC(unsigned char, *pcbOutBuffer); - if(pbWorkBuffer == NULL) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return 0; - } - } - - // Get the current compression index - nCompressIndex = nCompressCount - 1; - - // Perform all compressions in the array - for(int i = 0; i < nCompressCount; i++) - { - // Choose the proper output buffer - pbOutput = (nCompressIndex & 1) ? pbWorkBuffer : pbOutBuffer; - nCompressIndex--; - - // Perform the (next) compression - // Note that if the compression method is unable to compress the input data block - // by at least 2 bytes, we consider it as failure and will use source data instead - cbOutBuffer = *pcbOutBuffer - 1; - CompressFuncArray[i](pbOutput + 1, &cbOutBuffer, pbInput, cbInLength, &nCmpType, nCmpLevel); - - // If the compression failed, we copy the input buffer as-is. - // Note that there is one extra byte at the end of the intermediate buffer, so it should be OK - if(cbOutBuffer > (cbInLength - 2)) - { - memcpy(pbOutput + nAtLeastOneCompressionDone, pbInput, cbInLength); - cbOutBuffer = cbInLength; - } - else - { - // Remember that we have done at least one compression - nAtLeastOneCompressionDone = 1; - uCompressionMask |= CompressByte[i]; - } - - // Now point input buffer to the output buffer - pbInput = pbOutput + nAtLeastOneCompressionDone; - cbInLength = cbOutBuffer; - } - - // If at least one compression succeeded, put the compression - // mask to the begin of the output buffer - if(nAtLeastOneCompressionDone) - *pbOutBuffer = (unsigned char)uCompressionMask; - *pcbOutBuffer = cbOutBuffer + nAtLeastOneCompressionDone; - } - else - { - memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); - *pcbOutBuffer = cbInBuffer; - } - - // Cleanup and return - if(pbWorkBuffer != NULL) - STORM_FREE(pbWorkBuffer); - return nResult; -} - -/*****************************************************************************/ -/* */ -/* SCompDecompress */ -/* */ -/*****************************************************************************/ - -// This table contains decompress functions which can be applied to -// uncompressed data. The compression mask is stored in the first byte -// of compressed data -static TDecompressTable dcmp_table[] = -{ -#ifdef FULL - {MPQ_COMPRESSION_BZIP2, Decompress_BZIP2}, // Decompression with Bzip2 library -#endif - {MPQ_COMPRESSION_PKWARE, Decompress_PKLIB}, // Decompression with Pkware Data Compression Library -#ifdef FULL - {MPQ_COMPRESSION_ZLIB, Decompress_ZLIB}, // Decompression with the "zlib" library - {MPQ_COMPRESSION_HUFFMANN, Decompress_huff}, // Huffmann decompression - {MPQ_COMPRESSION_ADPCM_STEREO, Decompress_ADPCM_stereo}, // IMA ADPCM stereo decompression - {MPQ_COMPRESSION_ADPCM_MONO, Decompress_ADPCM_mono}, // IMA ADPCM mono decompression - {MPQ_COMPRESSION_SPARSE, Decompress_SPARSE} // Sparse decompression -#endif -}; - -int STORMAPI SCompDecompress(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - unsigned char * pbWorkBuffer = NULL; - unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer; - unsigned char * pbInBuffer = (unsigned char *)pvInBuffer; - unsigned char * pbOutput = (unsigned char *)pvOutBuffer; - unsigned char * pbInput; - unsigned uCompressionMask; // Decompressions applied to the data - unsigned uCompressionCopy; // Decompressions applied to the data - int cbOutBuffer = *pcbOutBuffer; // Current size of the output buffer - int cbInLength; // Current size of the input buffer - int nCompressCount = 0; // Number of compressions to be applied - int nCompressIndex = 0; - int nResult = 1; - - // Verify buffer sizes - if(cbOutBuffer < cbInBuffer || cbInBuffer < 1) - return 0; - - // If the input length is the same as output length, do nothing. - if(cbOutBuffer == cbInBuffer) - { - // If the buffers are equal, don't copy anything. - if(pvInBuffer != pvOutBuffer) - memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); - return 1; - } - - // Get applied compression types and decrement data length - uCompressionMask = uCompressionCopy = (unsigned char)*pbInBuffer++; - cbInBuffer--; - - // Get current compressed data and length of it - pbInput = pbInBuffer; - cbInLength = cbInBuffer; - - // This compression function doesn't support LZMA - assert(uCompressionMask != MPQ_COMPRESSION_LZMA); - - // Parse the compression mask - for(size_t i = 0; i < (sizeof(dcmp_table) / sizeof(TDecompressTable)); i++) - { - // If the mask agrees, insert the compression function to the array - if(uCompressionMask & dcmp_table[i].uMask) - { - uCompressionCopy &= ~dcmp_table[i].uMask; - nCompressCount++; - } - } - - // If at least one of the compressions remaing unknown, return an error - if(nCompressCount == 0 || uCompressionCopy != 0) - { - SetLastError(ERROR_NOT_SUPPORTED); - return 0; - } - - // If there is more than one compression, we have to allocate extra buffer - if(nCompressCount > 1) - { - pbWorkBuffer = STORM_ALLOC(unsigned char, cbOutBuffer); - if(pbWorkBuffer == NULL) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return 0; - } - } - - // Get the current compression index - nCompressIndex = nCompressCount - 1; - - // Apply all decompressions - for(size_t i = 0; i < (sizeof(dcmp_table) / sizeof(TDecompressTable)); i++) - { - // Perform the (next) decompression - if(uCompressionMask & dcmp_table[i].uMask) - { - // Get the correct output buffer - pbOutput = (nCompressIndex & 1) ? pbWorkBuffer : pbOutBuffer; - nCompressIndex--; - - // Perform the decompression - cbOutBuffer = *pcbOutBuffer; - nResult = dcmp_table[i].Decompress(pbOutput, &cbOutBuffer, pbInput, cbInLength); - if(nResult == 0 || cbOutBuffer == 0) - { - SetLastError(ERROR_FILE_CORRUPT); - nResult = 0; - break; - } - - // Switch buffers - cbInLength = cbOutBuffer; - pbInput = pbOutput; - } - } - - // Put the length of the decompressed data to the output buffer - *pcbOutBuffer = cbOutBuffer; - - // Cleanup and return - if(pbWorkBuffer != NULL) - STORM_FREE(pbWorkBuffer); - return nResult; -} - -int STORMAPI SCompDecompress2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - DECOMPRESS pfnDecompress1 = NULL; - DECOMPRESS pfnDecompress2 = NULL; - unsigned char * pbWorkBuffer = (unsigned char *)pvOutBuffer; - unsigned char * pbInBuffer = (unsigned char *)pvInBuffer; - int cbWorkBuffer = *pcbOutBuffer; - int nResult; - char CompressionMethod; - - // Verify buffer sizes - if(*pcbOutBuffer < cbInBuffer || cbInBuffer < 1) - return 0; - - // If the outputbuffer is as big as input buffer, just copy the block - if(*pcbOutBuffer == cbInBuffer) - { - if(pvOutBuffer != pvInBuffer) - memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); - return 1; - } - - // Get the compression methods - CompressionMethod = *pbInBuffer++; - cbInBuffer--; - - // We only recognize a fixed set of compression methods - switch((unsigned char)CompressionMethod) - { -#ifdef FULL - case MPQ_COMPRESSION_ZLIB: - pfnDecompress1 = Decompress_ZLIB; - break; -#endif - - case MPQ_COMPRESSION_PKWARE: - pfnDecompress1 = Decompress_PKLIB; - break; - -#ifdef FULL - case MPQ_COMPRESSION_BZIP2: - pfnDecompress1 = Decompress_BZIP2; - break; - - case MPQ_COMPRESSION_LZMA: - pfnDecompress1 = Decompress_LZMA; - break; - - case MPQ_COMPRESSION_SPARSE: - pfnDecompress1 = Decompress_SPARSE; - break; - - case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_ZLIB): - pfnDecompress1 = Decompress_ZLIB; - pfnDecompress2 = Decompress_SPARSE; - break; - - case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_BZIP2): - pfnDecompress1 = Decompress_BZIP2; - pfnDecompress2 = Decompress_SPARSE; - break; - - // - // Note: Any combination including MPQ_COMPRESSION_ADPCM_MONO, - // MPQ_COMPRESSION_ADPCM_STEREO or MPQ_COMPRESSION_HUFFMANN - // is not supported by newer MPQs. - // - - case (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_HUFFMANN): - pfnDecompress1 = Decompress_huff; - pfnDecompress2 = Decompress_ADPCM_mono; - break; - - case (MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN): - pfnDecompress1 = Decompress_huff; - pfnDecompress2 = Decompress_ADPCM_stereo; - break; -#endif - default: - SetLastError(ERROR_FILE_CORRUPT); - return 0; - } - - // If we have to use two decompressions, allocate temporary buffer - if(pfnDecompress2 != NULL) - { - pbWorkBuffer = STORM_ALLOC(unsigned char, *pcbOutBuffer); - if(pbWorkBuffer == NULL) - { - SetLastError(ERROR_NOT_ENOUGH_MEMORY); - return 0; - } - } - - // Apply the first decompression method - nResult = pfnDecompress1(pbWorkBuffer, &cbWorkBuffer, pbInBuffer, cbInBuffer); - - // Apply the second decompression method, if any - if(pfnDecompress2 != NULL && nResult != 0) - { - cbInBuffer = cbWorkBuffer; - cbWorkBuffer = *pcbOutBuffer; - nResult = pfnDecompress2(pvOutBuffer, &cbWorkBuffer, pbWorkBuffer, cbInBuffer); - } - - // Supply the output buffer size - *pcbOutBuffer = cbWorkBuffer; - - // Free temporary buffer - if(pbWorkBuffer != pvOutBuffer) - STORM_FREE(pbWorkBuffer); - - if(nResult == 0) - SetLastError(ERROR_FILE_CORRUPT); - return nResult; -} - -#ifdef FULL -/*****************************************************************************/ -/* */ -/* File decompression for MPK archives */ -/* */ -/*****************************************************************************/ - -int SCompDecompressMpk(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) -{ - return Decompress_LZMA_MPK(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer); -} -#endif - +/*****************************************************************************/ +/* SCompression.cpp Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* This module serves as a bridge between StormLib code and (de)compression */ +/* functions. All (de)compression calls go (and should only go) through this */ +/* module. No system headers should be included in this module to prevent */ +/* compile-time problems. */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 01.04.03 1.00 Lad The first version of SCompression.cpp */ +/* 19.11.03 1.01 Dan Big endian handling */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" + +//----------------------------------------------------------------------------- +// Local structures + +// Information about the input and output buffers for pklib +typedef struct +{ + unsigned char * pbInBuff; // Pointer to input data buffer + unsigned char * pbInBuffEnd; // End of the input buffer + unsigned char * pbOutBuff; // Pointer to output data buffer + unsigned char * pbOutBuffEnd; // Pointer to output data buffer +} TDataInfo; + +// Prototype of the compression function +// Function doesn't return an error. A success means that the size of compressed buffer +// is lower than size of uncompressed buffer. +typedef void (*COMPRESS)( + void * pvOutBuffer, // [out] Pointer to the buffer where the compressed data will be stored + int * pcbOutBuffer, // [in] Pointer to length of the buffer pointed by pvOutBuffer + void * pvInBuffer, // [in] Pointer to the buffer with data to compress + int cbInBuffer, // [in] Length of the buffer pointer by pvInBuffer + int * pCmpType, // [in] Compression-method specific value. ADPCM Setups this for the following Huffman compression + int nCmpLevel); // [in] Compression specific value. ADPCM uses this. Should be set to zero. + +// Prototype of the decompression function +// Returns 1 if success, 0 if failure +typedef int (*DECOMPRESS)( + void * pvOutBuffer, // [out] Pointer to the buffer where to store decompressed data + int * pcbOutBuffer, // [in] Pointer to total size of the buffer pointed by pvOutBuffer + // [out] Contains length of the decompressed data + void * pvInBuffer, // [in] Pointer to data to be decompressed + int cbInBuffer); // [in] Length of the data to be decompressed + +// Table of compression functions +typedef struct +{ + unsigned long uMask; // Compression mask + COMPRESS Compress; // Compression function +} TCompressTable; + +// Table of decompression functions +typedef struct +{ + unsigned long uMask; // Decompression bit + DECOMPRESS Decompress; // Decompression function +} TDecompressTable; + + +/*****************************************************************************/ +/* */ +/* Support for Huffman compression (0x01) */ +/* */ +/*****************************************************************************/ + +void Compress_huff(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) +{ + THuffmannTree ht(true); + TOutputStream os(pvOutBuffer, *pcbOutBuffer); + + STORMLIB_UNUSED(nCmpLevel); + *pcbOutBuffer = ht.Compress(&os, pvInBuffer, cbInBuffer, *pCmpType); +} + +int Decompress_huff(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ + THuffmannTree ht(false); + TInputStream is(pvInBuffer, cbInBuffer); + + *pcbOutBuffer = ht.Decompress(pvOutBuffer, *pcbOutBuffer, &is); + return (*pcbOutBuffer == 0) ? 0 : 1; +} + +/******************************************************************************/ +/* */ +/* Support for ZLIB compression (0x02) */ +/* */ +/******************************************************************************/ + +void Compress_ZLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) +{ + z_stream z; // Stream information for zlib + int windowBits; + int nResult; + + // Keep compilers happy + STORMLIB_UNUSED(pCmpType); + STORMLIB_UNUSED(nCmpLevel); + + // Fill the stream structure for zlib + z.next_in = (Bytef *)pvInBuffer; + z.avail_in = (uInt)cbInBuffer; + z.total_in = cbInBuffer; + z.next_out = (Bytef *)pvOutBuffer; + z.avail_out = *pcbOutBuffer; + z.total_out = 0; + z.zalloc = NULL; + z.zfree = NULL; + + // Determine the proper window bits (WoW.exe build 12694) + if(cbInBuffer <= 0x100) + windowBits = 8; + else if(cbInBuffer <= 0x200) + windowBits = 9; + else if(cbInBuffer <= 0x400) + windowBits = 10; + else if(cbInBuffer <= 0x800) + windowBits = 11; + else if(cbInBuffer <= 0x1000) + windowBits = 12; + else if(cbInBuffer <= 0x2000) + windowBits = 13; + else if(cbInBuffer <= 0x4000) + windowBits = 14; + else + windowBits = 15; + + // Initialize the compression. + // Storm.dll uses zlib version 1.1.3 + // Wow.exe uses zlib version 1.2.3 + nResult = deflateInit2(&z, + 6, // Compression level used by WoW MPQs + Z_DEFLATED, + windowBits, + 8, + Z_DEFAULT_STRATEGY); + if(nResult == Z_OK) + { + // Call zlib to compress the data + nResult = deflate(&z, Z_FINISH); + + if(nResult == Z_OK || nResult == Z_STREAM_END) + *pcbOutBuffer = z.total_out; + + deflateEnd(&z); + } +} + +int Decompress_ZLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ + z_stream z; // Stream information for zlib + int nResult; + + // Fill the stream structure for zlib + z.next_in = (Bytef *)pvInBuffer; + z.avail_in = (uInt)cbInBuffer; + z.total_in = cbInBuffer; + z.next_out = (Bytef *)pvOutBuffer; + z.avail_out = *pcbOutBuffer; + z.total_out = 0; + z.zalloc = NULL; + z.zfree = NULL; + + // Initialize the decompression structure. Storm.dll uses zlib version 1.1.3 + if((nResult = inflateInit(&z)) == 0) + { + // Call zlib to decompress the data + nResult = inflate(&z, Z_FINISH); + *pcbOutBuffer = z.total_out; + inflateEnd(&z); + } + return nResult; +} + +/******************************************************************************/ +/* */ +/* Support functions for PKWARE Data Compression Library compression (0x08) */ +/* */ +/******************************************************************************/ + +// Function loads data from the input buffer. Used by Pklib's "implode" +// and "explode" function as user-defined callback +// Returns number of bytes loaded +// +// char * buf - Pointer to a buffer where to store loaded data +// unsigned int * size - Max. number of bytes to read +// void * param - Custom pointer, parameter of implode/explode + +static unsigned int ReadInputData(char * buf, unsigned int * size, void * param) +{ + TDataInfo * pInfo = (TDataInfo *)param; + unsigned int nMaxAvail = (unsigned int)(pInfo->pbInBuffEnd - pInfo->pbInBuff); + unsigned int nToRead = *size; + + // Check the case when not enough data available + if(nToRead > nMaxAvail) + nToRead = nMaxAvail; + + // Load data and increment offsets + memcpy(buf, pInfo->pbInBuff, nToRead); + pInfo->pbInBuff += nToRead; + assert(pInfo->pbInBuff <= pInfo->pbInBuffEnd); + return nToRead; +} + +// Function for store output data. Used by Pklib's "implode" and "explode" +// as user-defined callback +// +// char * buf - Pointer to data to be written +// unsigned int * size - Number of bytes to write +// void * param - Custom pointer, parameter of implode/explode + +static void WriteOutputData(char * buf, unsigned int * size, void * param) +{ + TDataInfo * pInfo = (TDataInfo *)param; + unsigned int nMaxWrite = (unsigned int)(pInfo->pbOutBuffEnd - pInfo->pbOutBuff); + unsigned int nToWrite = *size; + + // Check the case when not enough space in the output buffer + if(nToWrite > nMaxWrite) + nToWrite = nMaxWrite; + + // Write output data and increments offsets + memcpy(pInfo->pbOutBuff, buf, nToWrite); + pInfo->pbOutBuff += nToWrite; + assert(pInfo->pbOutBuff <= pInfo->pbOutBuffEnd); +} + +static void Compress_PKLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) +{ + TDataInfo Info; // Data information + char * work_buf = STORM_ALLOC(char, CMP_BUFFER_SIZE);// Pklib's work buffer + unsigned int dict_size; // Dictionary size + unsigned int ctype = CMP_BINARY; // Compression type + + // Keep compilers happy + STORMLIB_UNUSED(pCmpType); + STORMLIB_UNUSED(nCmpLevel); + + // Handle no-memory condition + if(work_buf != NULL) + { + // Fill data information structure + memset(work_buf, 0, CMP_BUFFER_SIZE); + Info.pbInBuff = (unsigned char *)pvInBuffer; + Info.pbInBuffEnd = (unsigned char *)pvInBuffer + cbInBuffer; + Info.pbOutBuff = (unsigned char *)pvOutBuffer; + Info.pbOutBuffEnd = (unsigned char *)pvOutBuffer + *pcbOutBuffer; + + // + // Set the dictionary size + // + // Diablo I uses fixed dictionary size of CMP_IMPLODE_DICT_SIZE3 + // Starcraft I uses the variable dictionary size based on algorithm below + // + + if (cbInBuffer < 0x600) + dict_size = CMP_IMPLODE_DICT_SIZE1; + else if(0x600 <= cbInBuffer && cbInBuffer < 0xC00) + dict_size = CMP_IMPLODE_DICT_SIZE2; + else + dict_size = CMP_IMPLODE_DICT_SIZE3; + + // Do the compression + if(implode(ReadInputData, WriteOutputData, work_buf, &Info, &ctype, &dict_size) == CMP_NO_ERROR) + *pcbOutBuffer = (int)(Info.pbOutBuff - (unsigned char *)pvOutBuffer); + + STORM_FREE(work_buf); + } +} + +static int Decompress_PKLIB(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ + TDataInfo Info; // Data information + char * work_buf = STORM_ALLOC(char, EXP_BUFFER_SIZE);// Pklib's work buffer + + // Handle no-memory condition + if(work_buf == NULL) + return 0; + + // Fill data information structure + memset(work_buf, 0, EXP_BUFFER_SIZE); + Info.pbInBuff = (unsigned char *)pvInBuffer; + Info.pbInBuffEnd = (unsigned char *)pvInBuffer + cbInBuffer; + Info.pbOutBuff = (unsigned char *)pvOutBuffer; + Info.pbOutBuffEnd = (unsigned char *)pvOutBuffer + *pcbOutBuffer; + + // Do the decompression + explode(ReadInputData, WriteOutputData, work_buf, &Info); + + // If PKLIB is unable to decompress the data, return 0; + if(Info.pbOutBuff == pvOutBuffer) + { + STORM_FREE(work_buf); + return 0; + } + + // Give away the number of decompressed bytes + *pcbOutBuffer = (int)(Info.pbOutBuff - (unsigned char *)pvOutBuffer); + STORM_FREE(work_buf); + return 1; +} + +/******************************************************************************/ +/* */ +/* Support for Bzip2 compression (0x10) */ +/* */ +/******************************************************************************/ + +static void Compress_BZIP2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) +{ + bz_stream strm; + int blockSize100k = 9; + int workFactor = 30; + int bzError; + + // Keep compilers happy + STORMLIB_UNUSED(pCmpType); + STORMLIB_UNUSED(nCmpLevel); + + // Initialize the BZIP2 compression + strm.bzalloc = NULL; + strm.bzfree = NULL; + strm.opaque = NULL; + + // Blizzard uses 9 as blockSize100k, (0x30 as workFactor) + // Last checked on Starcraft II + if(BZ2_bzCompressInit(&strm, blockSize100k, 0, workFactor) == BZ_OK) + { + strm.next_in = (char *)pvInBuffer; + strm.avail_in = cbInBuffer; + strm.next_out = (char *)pvOutBuffer; + strm.avail_out = *pcbOutBuffer; + + // Perform the compression + for(;;) + { + bzError = BZ2_bzCompress(&strm, (strm.avail_in != 0) ? BZ_RUN : BZ_FINISH); + if(bzError == BZ_STREAM_END || bzError < 0) + break; + } + + // Put the stream into idle state + BZ2_bzCompressEnd(&strm); + + if(bzError > 0) + *pcbOutBuffer = strm.total_out_lo32; + } +} + +static int Decompress_BZIP2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ + bz_stream strm; + int nResult = BZ_OK; + + // Initialize the BZIP2 decompression + strm.bzalloc = NULL; + strm.bzfree = NULL; + strm.opaque = NULL; + + // Initialize decompression + if(BZ2_bzDecompressInit(&strm, 0, 0) == BZ_OK) + { + strm.next_in = (char *)pvInBuffer; + strm.avail_in = cbInBuffer; + strm.next_out = (char *)pvOutBuffer; + strm.avail_out = *pcbOutBuffer; + + // Perform the decompression + while(nResult != BZ_STREAM_END) + { + nResult = BZ2_bzDecompress(&strm); + + // If any error there, break the loop + if(nResult < BZ_OK) + break; + } + + // Put the stream into idle state + BZ2_bzDecompressEnd(&strm); + + // If all succeeded, set the number of output bytes + if(nResult >= BZ_OK) + { + *pcbOutBuffer = strm.total_out_lo32; + return 1; + } + } + + // Something failed, so set number of output bytes to zero + *pcbOutBuffer = 0; + return 1; +} + +/******************************************************************************/ +/* */ +/* Support functions for LZMA compression (0x12) */ +/* */ +/******************************************************************************/ + +#define LZMA_HEADER_SIZE (1 + LZMA_PROPS_SIZE + 8) + +static SRes LZMA_Callback_Progress(void * /* p */, UInt64 /* inSize */, UInt64 /* outSize */) +{ + return SZ_OK; +} + +static void * LZMA_Callback_Alloc(void *p, size_t size) +{ + p = p; + return STORM_ALLOC(BYTE, size); +} + +/* address can be 0 */ +static void LZMA_Callback_Free(void *p, void *address) +{ + p = p; + if(address != NULL) + STORM_FREE(address); +} + +// +// Note: So far, I haven't seen any files compressed by LZMA. +// This code haven't been verified against code ripped from Starcraft II Beta, +// but we know that Starcraft LZMA decompression code is able to decompress +// the data compressed by StormLib. +// + +static void Compress_LZMA(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) +{ + ICompressProgress Progress; + CLzmaEncProps props; + ISzAlloc SzAlloc; + Byte * pbOutBuffer = (Byte *)pvOutBuffer; + Byte * destBuffer; + SizeT destLen = *pcbOutBuffer; + SizeT srcLen = cbInBuffer; + Byte encodedProps[LZMA_PROPS_SIZE]; + size_t encodedPropsSize = LZMA_PROPS_SIZE; + SRes nResult; + + // Keep compilers happy + STORMLIB_UNUSED(pCmpType); + STORMLIB_UNUSED(nCmpLevel); + + // Fill the callbacks in structures + Progress.Progress = LZMA_Callback_Progress; + SzAlloc.Alloc = LZMA_Callback_Alloc; + SzAlloc.Free = LZMA_Callback_Free; + + // Initialize properties + LzmaEncProps_Init(&props); + + // Perform compression + destBuffer = (Byte *)pvOutBuffer + LZMA_HEADER_SIZE; + destLen = *pcbOutBuffer - LZMA_HEADER_SIZE; + nResult = LzmaEncode(destBuffer, + &destLen, + (Byte *)pvInBuffer, + srcLen, + &props, + encodedProps, + &encodedPropsSize, + 0, + &Progress, + &SzAlloc, + &SzAlloc); + if(nResult != SZ_OK) + return; + + // If we failed to compress the data + if(destLen >= (SizeT)(*pcbOutBuffer - LZMA_HEADER_SIZE)) + return; + + // Write "useFilter" variable. Blizzard MPQ must not use filter. + *pbOutBuffer++ = 0; + + // Copy the encoded properties to the output buffer + memcpy(pvOutBuffer, encodedProps, encodedPropsSize); + pbOutBuffer += encodedPropsSize; + + // Copy the size of the data + *pbOutBuffer++ = (unsigned char)(srcLen >> 0x00); + *pbOutBuffer++ = (unsigned char)(srcLen >> 0x08); + *pbOutBuffer++ = (unsigned char)(srcLen >> 0x10); + *pbOutBuffer++ = (unsigned char)(srcLen >> 0x18); + *pbOutBuffer++ = 0; + *pbOutBuffer++ = 0; + *pbOutBuffer++ = 0; + *pbOutBuffer++ = 0; + + // Give the size of the data to the caller + *pcbOutBuffer = (unsigned int)(destLen + LZMA_HEADER_SIZE); +} + +static int Decompress_LZMA(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ + ELzmaStatus LzmaStatus; + ISzAlloc SzAlloc; + Byte * destBuffer = (Byte *)pvOutBuffer; + Byte * srcBuffer = (Byte *)pvInBuffer; + SizeT destLen = *pcbOutBuffer; + SizeT srcLen = cbInBuffer; + SRes nResult; + + // There must be at least 0x0E bytes in the buffer + if(srcLen <= LZMA_HEADER_SIZE) + return 0; + + // We only accept blocks that have no filter used + if(*srcBuffer != 0) + return 0; + + // Fill the callbacks in structures + SzAlloc.Alloc = LZMA_Callback_Alloc; + SzAlloc.Free = LZMA_Callback_Free; + + // Perform compression + srcLen = cbInBuffer - LZMA_HEADER_SIZE; + nResult = LzmaDecode(destBuffer, + &destLen, + srcBuffer + LZMA_HEADER_SIZE, + &srcLen, + srcBuffer + 1, + LZMA_PROPS_SIZE, + LZMA_FINISH_END, + &LzmaStatus, + &SzAlloc); + if(nResult != SZ_OK) + return 0; + + *pcbOutBuffer = (unsigned int)destLen; + return 1; +} + +static int Decompress_LZMA_MPK(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ + ELzmaStatus LzmaStatus; + ISzAlloc SzAlloc; + Byte * destBuffer = (Byte *)pvOutBuffer; + Byte * srcBuffer = (Byte *)pvInBuffer; + SizeT destLen = *pcbOutBuffer; + SizeT srcLen = cbInBuffer; + SRes nResult; + BYTE LZMA_Props[] = {0x5D, 0x00, 0x00, 0x00, 0x01}; + + // There must be at least 0x0E bytes in the buffer + if(srcLen <= sizeof(LZMA_Props)) + return 0; + + // Verify the props header + if(memcmp(pvInBuffer, LZMA_Props, sizeof(LZMA_Props))) + return 0; + + // Fill the callbacks in structures + SzAlloc.Alloc = LZMA_Callback_Alloc; + SzAlloc.Free = LZMA_Callback_Free; + + // Perform compression + srcLen = cbInBuffer - sizeof(LZMA_Props); + nResult = LzmaDecode(destBuffer, + &destLen, + srcBuffer + sizeof(LZMA_Props), + &srcLen, + srcBuffer, + sizeof(LZMA_Props), + LZMA_FINISH_END, + &LzmaStatus, + &SzAlloc); + if(nResult != SZ_OK) + return 0; + + *pcbOutBuffer = (unsigned int)destLen; + return 1; +} + +/******************************************************************************/ +/* */ +/* Support functions for SPARSE compression (0x20) */ +/* */ +/******************************************************************************/ + +void Compress_SPARSE(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) +{ + // Keep compilers happy + STORMLIB_UNUSED(pCmpType); + STORMLIB_UNUSED(nCmpLevel); + + CompressSparse(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer); +} + +int Decompress_SPARSE(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ + return DecompressSparse(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer); +} + +/******************************************************************************/ +/* */ +/* Support for ADPCM mono compression (0x40) */ +/* */ +/******************************************************************************/ + +static void Compress_ADPCM_mono(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) +{ + // Prepare the compression level for Huffmann compression, + // which will be called as next step + if(0 < nCmpLevel && nCmpLevel <= 2) + { + nCmpLevel = 4; + *pCmpType = 6; + } + else if(nCmpLevel == 3) + { + nCmpLevel = 6; + *pCmpType = 8; + } + else + { + nCmpLevel = 5; + *pCmpType = 7; + } + *pcbOutBuffer = CompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 1, nCmpLevel); +} + +static int Decompress_ADPCM_mono(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ + *pcbOutBuffer = DecompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 1); + return 1; +} + +/******************************************************************************/ +/* */ +/* Support for ADPCM stereo compression (0x80) */ +/* */ +/******************************************************************************/ + +static void Compress_ADPCM_stereo(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, int * pCmpType, int nCmpLevel) +{ + // Prepare the compression level for Huffmann compression, + // which will be called as next step + if(0 < nCmpLevel && nCmpLevel <= 2) + { + nCmpLevel = 4; + *pCmpType = 6; + } + else if(nCmpLevel == 3) + { + nCmpLevel = 6; + *pCmpType = 8; + } + else + { + nCmpLevel = 5; + *pCmpType = 7; + } + *pcbOutBuffer = CompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 2, nCmpLevel); +} + +static int Decompress_ADPCM_stereo(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ + *pcbOutBuffer = DecompressADPCM(pvOutBuffer, *pcbOutBuffer, pvInBuffer, cbInBuffer, 2); + return 1; +} + +/*****************************************************************************/ +/* */ +/* SCompImplode */ +/* */ +/*****************************************************************************/ + +int WINAPI SCompImplode(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ + int cbOutBuffer; + + // Check for valid parameters + if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + // Perform the compression + cbOutBuffer = *pcbOutBuffer; + Compress_PKLIB(pvOutBuffer, &cbOutBuffer, pvInBuffer, cbInBuffer, NULL, 0); + + // If the compression was unsuccessful, copy the data as-is + if(cbOutBuffer >= *pcbOutBuffer) + { + memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); + cbOutBuffer = *pcbOutBuffer; + } + + *pcbOutBuffer = cbOutBuffer; + return 1; +} + +/*****************************************************************************/ +/* */ +/* SCompExplode */ +/* */ +/*****************************************************************************/ + +int WINAPI SCompExplode(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ + int cbOutBuffer; + + // Check for valid parameters + if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + // If the input length is the same as output length, do nothing. + cbOutBuffer = *pcbOutBuffer; + if(cbInBuffer == cbOutBuffer) + { + // If the buffers are equal, don't copy anything. + if(pvInBuffer == pvOutBuffer) + return 1; + + memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); + return 1; + } + + // Perform decompression + if(!Decompress_PKLIB(pvOutBuffer, &cbOutBuffer, pvInBuffer, cbInBuffer)) + { + SetLastError(ERROR_FILE_CORRUPT); + return 0; + } + + *pcbOutBuffer = cbOutBuffer; + return 1; +} + +/*****************************************************************************/ +/* */ +/* SCompCompress */ +/* */ +/*****************************************************************************/ + +// This table contains compress functions which can be applied to +// uncompressed data. Each bit means the corresponding +// compression method/function must be applied. +// +// WAVes compression Data compression +// ------------------ ------------------- +// 1st sector - 0x08 0x08 (D, HF, W2, SC, D2) +// Next sectors - 0x81 0x02 (W3) + +static TCompressTable cmp_table[] = +{ + {MPQ_COMPRESSION_SPARSE, Compress_SPARSE}, // Sparse compression + {MPQ_COMPRESSION_ADPCM_MONO, Compress_ADPCM_mono}, // IMA ADPCM mono compression + {MPQ_COMPRESSION_ADPCM_STEREO, Compress_ADPCM_stereo}, // IMA ADPCM stereo compression + {MPQ_COMPRESSION_HUFFMANN, Compress_huff}, // Huffmann compression + {MPQ_COMPRESSION_ZLIB, Compress_ZLIB}, // Compression with the "zlib" library + {MPQ_COMPRESSION_PKWARE, Compress_PKLIB}, // Compression with Pkware DCL + {MPQ_COMPRESSION_BZIP2, Compress_BZIP2} // Compression Bzip2 library +}; + +int WINAPI SCompCompress(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, unsigned uCompressionMask, int nCmpType, int nCmpLevel) +{ + COMPRESS CompressFuncArray[0x10]; // Array of compression functions, applied sequentially + unsigned char CompressByte[0x10]; // CompressByte for each method in the CompressFuncArray array + unsigned char * pbWorkBuffer = NULL; // Temporary storage for decompressed data + unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer; + unsigned char * pbOutput = (unsigned char *)pvOutBuffer;// Current output buffer + unsigned char * pbInput = (unsigned char *)pvInBuffer; // Current input buffer + int nCompressCount = 0; + int nCompressIndex = 0; + int nAtLeastOneCompressionDone = 0; + int cbOutBuffer = 0; + int cbInLength = cbInBuffer; + int nResult = 1; + + // Check for valid parameters + if(!pcbOutBuffer || *pcbOutBuffer < cbInBuffer || !pvOutBuffer || !pvInBuffer) + { + SetLastError(ERROR_INVALID_PARAMETER); + return 0; + } + + // Zero input length brings zero output length + if(cbInBuffer == 0) + { + *pcbOutBuffer = 0; + return true; + } + + // Setup the compression function array + if(uCompressionMask == MPQ_COMPRESSION_LZMA) + { + CompressFuncArray[0] = Compress_LZMA; + CompressByte[0] = (char)uCompressionMask; + nCompressCount = 1; + } + else + { + // Fill the compressions array + for(size_t i = 0; i < (sizeof(cmp_table) / sizeof(TCompressTable)); i++) + { + // If the mask agrees, insert the compression function to the array + if(uCompressionMask & cmp_table[i].uMask) + { + CompressFuncArray[nCompressCount] = cmp_table[i].Compress; + CompressByte[nCompressCount] = (unsigned char)cmp_table[i].uMask; + uCompressionMask &= ~cmp_table[i].uMask; + nCompressCount++; + } + } + + // If at least one of the compressions remaing unknown, return an error + if(uCompressionMask != 0) + { + SetLastError(ERROR_NOT_SUPPORTED); + return 0; + } + } + + // If there is at least one compression, do it + if(nCompressCount > 0) + { + // If we need to do more than 1 compression, allocate intermediate buffer + if(nCompressCount > 1) + { + pbWorkBuffer = STORM_ALLOC(unsigned char, *pcbOutBuffer); + if(pbWorkBuffer == NULL) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return 0; + } + } + + // Get the current compression index + nCompressIndex = nCompressCount - 1; + + // Perform all compressions in the array + for(int i = 0; i < nCompressCount; i++) + { + // Choose the proper output buffer + pbOutput = (nCompressIndex & 1) ? pbWorkBuffer : pbOutBuffer; + nCompressIndex--; + + // Perform the (next) compression + // Note that if the compression method is unable to compress the input data block + // by at least 2 bytes, we consider it as failure and will use source data instead + cbOutBuffer = *pcbOutBuffer - 1; + CompressFuncArray[i](pbOutput + 1, &cbOutBuffer, pbInput, cbInLength, &nCmpType, nCmpLevel); + + // If the compression failed, we copy the input buffer as-is. + // Note that there is one extra byte at the end of the intermediate buffer, so it should be OK + if(cbOutBuffer > (cbInLength - 2)) + { + memcpy(pbOutput + nAtLeastOneCompressionDone, pbInput, cbInLength); + cbOutBuffer = cbInLength; + } + else + { + // Remember that we have done at least one compression + nAtLeastOneCompressionDone = 1; + uCompressionMask |= CompressByte[i]; + } + + // Now point input buffer to the output buffer + pbInput = pbOutput + nAtLeastOneCompressionDone; + cbInLength = cbOutBuffer; + } + + // If at least one compression succeeded, put the compression + // mask to the begin of the output buffer + if(nAtLeastOneCompressionDone) + *pbOutBuffer = (unsigned char)uCompressionMask; + *pcbOutBuffer = cbOutBuffer + nAtLeastOneCompressionDone; + } + else + { + memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); + *pcbOutBuffer = cbInBuffer; + } + + // Cleanup and return + if(pbWorkBuffer != NULL) + STORM_FREE(pbWorkBuffer); + return nResult; +} + +/*****************************************************************************/ +/* */ +/* SCompDecompress */ +/* */ +/*****************************************************************************/ + +// This table contains decompress functions which can be applied to +// uncompressed data. The compression mask is stored in the first byte +// of compressed data +static TDecompressTable dcmp_table[] = +{ + {MPQ_COMPRESSION_BZIP2, Decompress_BZIP2}, // Decompression with Bzip2 library + {MPQ_COMPRESSION_PKWARE, Decompress_PKLIB}, // Decompression with Pkware Data Compression Library + {MPQ_COMPRESSION_ZLIB, Decompress_ZLIB}, // Decompression with the "zlib" library + {MPQ_COMPRESSION_HUFFMANN, Decompress_huff}, // Huffmann decompression + {MPQ_COMPRESSION_ADPCM_STEREO, Decompress_ADPCM_stereo}, // IMA ADPCM stereo decompression + {MPQ_COMPRESSION_ADPCM_MONO, Decompress_ADPCM_mono}, // IMA ADPCM mono decompression + {MPQ_COMPRESSION_SPARSE, Decompress_SPARSE} // Sparse decompression +}; + +int WINAPI SCompDecompress(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ + unsigned char * pbWorkBuffer = NULL; + unsigned char * pbOutBuffer = (unsigned char *)pvOutBuffer; + unsigned char * pbInBuffer = (unsigned char *)pvInBuffer; + unsigned char * pbOutput = (unsigned char *)pvOutBuffer; + unsigned char * pbInput; + unsigned uCompressionMask; // Decompressions applied to the data + unsigned uCompressionCopy; // Decompressions applied to the data + int cbOutBuffer = *pcbOutBuffer; // Current size of the output buffer + int cbInLength; // Current size of the input buffer + int nCompressCount = 0; // Number of compressions to be applied + int nCompressIndex = 0; + int nResult = 1; + + // Verify buffer sizes + if(cbOutBuffer < cbInBuffer || cbInBuffer < 1) + return 0; + + // If the input length is the same as output length, do nothing. + if(cbOutBuffer == cbInBuffer) + { + // If the buffers are equal, don't copy anything. + if(pvInBuffer != pvOutBuffer) + memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); + return 1; + } + + // Get applied compression types and decrement data length + uCompressionMask = uCompressionCopy = (unsigned char)*pbInBuffer++; + cbInBuffer--; + + // Get current compressed data and length of it + pbInput = pbInBuffer; + cbInLength = cbInBuffer; + + // This compression function doesn't support LZMA + assert(uCompressionMask != MPQ_COMPRESSION_LZMA); + + // Parse the compression mask + for(size_t i = 0; i < (sizeof(dcmp_table) / sizeof(TDecompressTable)); i++) + { + // If the mask agrees, insert the compression function to the array + if(uCompressionMask & dcmp_table[i].uMask) + { + uCompressionCopy &= ~dcmp_table[i].uMask; + nCompressCount++; + } + } + + // If at least one of the compressions remaing unknown, return an error + if(nCompressCount == 0 || uCompressionCopy != 0) + { + SetLastError(ERROR_NOT_SUPPORTED); + return 0; + } + + // If there is more than one compression, we have to allocate extra buffer + if(nCompressCount > 1) + { + pbWorkBuffer = STORM_ALLOC(unsigned char, cbOutBuffer); + if(pbWorkBuffer == NULL) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return 0; + } + } + + // Get the current compression index + nCompressIndex = nCompressCount - 1; + + // Apply all decompressions + for(size_t i = 0; i < (sizeof(dcmp_table) / sizeof(TDecompressTable)); i++) + { + // Perform the (next) decompression + if(uCompressionMask & dcmp_table[i].uMask) + { + // Get the correct output buffer + pbOutput = (nCompressIndex & 1) ? pbWorkBuffer : pbOutBuffer; + nCompressIndex--; + + // Perform the decompression + cbOutBuffer = *pcbOutBuffer; + nResult = dcmp_table[i].Decompress(pbOutput, &cbOutBuffer, pbInput, cbInLength); + if(nResult == 0 || cbOutBuffer == 0) + { + SetLastError(ERROR_FILE_CORRUPT); + nResult = 0; + break; + } + + // Switch buffers + cbInLength = cbOutBuffer; + pbInput = pbOutput; + } + } + + // Put the length of the decompressed data to the output buffer + *pcbOutBuffer = cbOutBuffer; + + // Cleanup and return + if(pbWorkBuffer != NULL) + STORM_FREE(pbWorkBuffer); + return nResult; +} + +int WINAPI SCompDecompress2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ + DECOMPRESS pfnDecompress1 = NULL; + DECOMPRESS pfnDecompress2 = NULL; + unsigned char * pbWorkBuffer = (unsigned char *)pvOutBuffer; + unsigned char * pbInBuffer = (unsigned char *)pvInBuffer; + int cbWorkBuffer = *pcbOutBuffer; + int nResult; + char CompressionMethod; + + // Verify buffer sizes + if(*pcbOutBuffer < cbInBuffer || cbInBuffer < 1) + return 0; + + // If the outputbuffer is as big as input buffer, just copy the block + if(*pcbOutBuffer == cbInBuffer) + { + if(pvOutBuffer != pvInBuffer) + memcpy(pvOutBuffer, pvInBuffer, cbInBuffer); + return 1; + } + + // Get the compression methods + CompressionMethod = *pbInBuffer++; + cbInBuffer--; + + // We only recognize a fixed set of compression methods + switch((unsigned char)CompressionMethod) + { + case MPQ_COMPRESSION_ZLIB: + pfnDecompress1 = Decompress_ZLIB; + break; + + case MPQ_COMPRESSION_PKWARE: + pfnDecompress1 = Decompress_PKLIB; + break; + + case MPQ_COMPRESSION_BZIP2: + pfnDecompress1 = Decompress_BZIP2; + break; + + case MPQ_COMPRESSION_LZMA: + pfnDecompress1 = Decompress_LZMA; + break; + + case MPQ_COMPRESSION_SPARSE: + pfnDecompress1 = Decompress_SPARSE; + break; + + case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_ZLIB): + pfnDecompress1 = Decompress_ZLIB; + pfnDecompress2 = Decompress_SPARSE; + break; + + case (MPQ_COMPRESSION_SPARSE | MPQ_COMPRESSION_BZIP2): + pfnDecompress1 = Decompress_BZIP2; + pfnDecompress2 = Decompress_SPARSE; + break; + + // + // Note: Any combination including MPQ_COMPRESSION_ADPCM_MONO, + // MPQ_COMPRESSION_ADPCM_STEREO or MPQ_COMPRESSION_HUFFMANN + // is not supported by newer MPQs. + // + + case (MPQ_COMPRESSION_ADPCM_MONO | MPQ_COMPRESSION_HUFFMANN): + pfnDecompress1 = Decompress_huff; + pfnDecompress2 = Decompress_ADPCM_mono; + break; + + case (MPQ_COMPRESSION_ADPCM_STEREO | MPQ_COMPRESSION_HUFFMANN): + pfnDecompress1 = Decompress_huff; + pfnDecompress2 = Decompress_ADPCM_stereo; + break; + + default: + SetLastError(ERROR_FILE_CORRUPT); + return 0; + } + + // If we have to use two decompressions, allocate temporary buffer + if(pfnDecompress2 != NULL) + { + pbWorkBuffer = STORM_ALLOC(unsigned char, *pcbOutBuffer); + if(pbWorkBuffer == NULL) + { + SetLastError(ERROR_NOT_ENOUGH_MEMORY); + return 0; + } + } + + // Apply the first decompression method + nResult = pfnDecompress1(pbWorkBuffer, &cbWorkBuffer, pbInBuffer, cbInBuffer); + + // Apply the second decompression method, if any + if(pfnDecompress2 != NULL && nResult != 0) + { + cbInBuffer = cbWorkBuffer; + cbWorkBuffer = *pcbOutBuffer; + nResult = pfnDecompress2(pvOutBuffer, &cbWorkBuffer, pbWorkBuffer, cbInBuffer); + } + + // Supply the output buffer size + *pcbOutBuffer = cbWorkBuffer; + + // Free temporary buffer + if(pbWorkBuffer != pvOutBuffer) + STORM_FREE(pbWorkBuffer); + + if(nResult == 0) + SetLastError(ERROR_FILE_CORRUPT); + return nResult; +} + +/*****************************************************************************/ +/* */ +/* File decompression for MPK archives */ +/* */ +/*****************************************************************************/ + +int SCompDecompressMpk(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer) +{ + return Decompress_LZMA_MPK(pvOutBuffer, pcbOutBuffer, pvInBuffer, cbInBuffer); +} + diff --git a/3rdParty/StormLib/src/SFileExtractFile.cpp b/3rdParty/StormLib/src/SFileExtractFile.cpp index c7e3c63bd..cabde4927 100644 --- a/3rdParty/StormLib/src/SFileExtractFile.cpp +++ b/3rdParty/StormLib/src/SFileExtractFile.cpp @@ -12,7 +12,7 @@ #include "StormLib.h" #include "StormCommon.h" -bool STORMAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const TCHAR * szExtracted, DWORD dwSearchScope) +bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const TCHAR * szExtracted, DWORD dwSearchScope) { TFileStream * pLocalFile = NULL; HANDLE hMpqFile = NULL; diff --git a/3rdParty/StormLib/src/SFileFindFile.cpp b/3rdParty/StormLib/src/SFileFindFile.cpp index 8ae75683e..3dbb2f83a 100644 --- a/3rdParty/StormLib/src/SFileFindFile.cpp +++ b/3rdParty/StormLib/src/SFileFindFile.cpp @@ -39,7 +39,7 @@ static TMPQSearch * IsValidSearchHandle(HANDLE hFind) return NULL; } -bool CheckWildCard(const char * szString, const char * szWildCard) +bool SFileCheckWildCard(const char * szString, const char * szWildCard) { const char * szWildCardPtr; @@ -71,7 +71,7 @@ bool CheckWildCard(const char * szString, const char * szWildCard) if(AsciiToUpperTable[szWildCardPtr[0]] == AsciiToUpperTable[szString[0]]) { - if(CheckWildCard(szString, szWildCardPtr)) + if(SFileCheckWildCard(szString, szWildCardPtr)) return true; } } @@ -222,6 +222,10 @@ static bool DoMPQSearch_FileEntry( // Is it a file but not a patch file? if((pFileEntry->dwFlags & hs->dwFlagMask) == MPQ_FILE_EXISTS) { + // Ignore fake files which are not compressed but have size higher than the archive + if ((pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) == 0 && (pFileEntry->dwFileSize > ha->FileSize)) + return false; + // Now we have to check if this file was not enumerated before if(!FileWasFoundBefore(ha, hs, pFileEntry)) { @@ -234,13 +238,15 @@ static bool DoMPQSearch_FileEntry( // Prepare the block index dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable); + if(dwBlockIndex == 569) + szNameBuff[0] = 'F'; // Get the file name. If it's not known, we will create pseudo-name szFileName = pFileEntry->szFileName; if(szFileName == NULL) { // Open the file by its pseudo-name. - sprintf(szNameBuff, "File%08u.xxx", (unsigned int)dwBlockIndex); + StringCreatePseudoFileName(szNameBuff, _countof(szNameBuff), dwBlockIndex, "xxx"); if(SFileOpenFileEx((HANDLE)hs->ha, szNameBuff, SFILE_OPEN_BASE_FILE, &hFile)) { SFileGetFileName(hFile, szNameBuff); @@ -253,7 +259,7 @@ static bool DoMPQSearch_FileEntry( if(szFileName != NULL) { // Check the file name against the wildcard - if(CheckWildCard(szFileName + nPrefixLength, hs->szSearchMask)) + if(SFileCheckWildCard(szFileName + nPrefixLength, hs->szSearchMask)) { // Fill the found entry. hash entry and block index are taken from the base MPQ lpFindFileData->dwHashIndex = HASH_ENTRY_FREE; @@ -378,7 +384,7 @@ static void FreeMPQSearch(TMPQSearch *& hs) //----------------------------------------------------------------------------- // Public functions -HANDLE STORMAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DATA * lpFindFileData, const TCHAR * szListFile) +HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DATA * lpFindFileData, const TCHAR * szListFile) { TMPQArchive * ha = (TMPQArchive *)hMpq; TMPQSearch * hs = NULL; @@ -391,13 +397,11 @@ HANDLE STORMAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_ if(szMask == NULL || lpFindFileData == NULL) nError = ERROR_INVALID_PARAMETER; -#ifdef FULL // Include the listfile into the MPQ's internal listfile // Note that if the listfile name is NULL, do nothing because the // internal listfile is always included. if(nError == ERROR_SUCCESS && szListFile != NULL && *szListFile != 0) nError = SFileAddListFile((HANDLE)ha, szListFile); -#endif // Allocate the structure for MPQ search if(nError == ERROR_SUCCESS) @@ -446,7 +450,7 @@ HANDLE STORMAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_ return (HANDLE)hs; } -bool STORMAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData) +bool WINAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData) { TMPQSearch * hs = IsValidSearchHandle(hFind); int nError = ERROR_SUCCESS; @@ -465,7 +469,7 @@ bool STORMAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData) return (nError == ERROR_SUCCESS); } -bool STORMAPI SFileFindClose(HANDLE hFind) +bool WINAPI SFileFindClose(HANDLE hFind) { TMPQSearch * hs = IsValidSearchHandle(hFind); diff --git a/3rdParty/StormLib/src/SFileGetFileInfo.cpp b/3rdParty/StormLib/src/SFileGetFileInfo.cpp index 8fe98066d..97610b22f 100644 --- a/3rdParty/StormLib/src/SFileGetFileInfo.cpp +++ b/3rdParty/StormLib/src/SFileGetFileInfo.cpp @@ -12,36 +12,9 @@ #include "StormLib.h" #include "StormCommon.h" -//----------------------------------------------------------------------------- -// Local defines - -// Information types for SFileGetFileInfo -#define SFILE_INFO_TYPE_INVALID_HANDLE 0 -#define SFILE_INFO_TYPE_NOT_FOUND 1 -#define SFILE_INFO_TYPE_DIRECT_POINTER 2 -#define SFILE_INFO_TYPE_ALLOCATED 3 -#define SFILE_INFO_TYPE_READ_FROM_FILE 4 -#define SFILE_INFO_TYPE_TABLE_POINTER 5 -#define SFILE_INFO_TYPE_FILE_ENTRY 6 - //----------------------------------------------------------------------------- // Local functions -static void ConvertFileEntryToSelfRelative(TFileEntry * pFileEntry, TFileEntry * pSrcFileEntry) -{ - // Copy the file entry itself - memcpy(pFileEntry, pSrcFileEntry, sizeof(TFileEntry)); - - // If source is NULL, leave it NULL - if(pSrcFileEntry->szFileName != NULL) - { - // Set the file name pointer after the file entry - pFileEntry->szFileName = (char *)(pFileEntry + 1); - strcpy(pFileEntry->szFileName, pSrcFileEntry->szFileName); - } -} - - static DWORD GetMpqFileCount(TMPQArchive * ha) { TFileEntry * pFileTableEnd; @@ -69,56 +42,138 @@ static DWORD GetMpqFileCount(TMPQArchive * ha) return dwFileCount; } -static bool GetFilePatchChain(TMPQFile * hf, void * pvFileInfo, DWORD cbFileInfo, DWORD * pcbLengthNeeded) +static bool GetInfo_ReturnError(DWORD dwErrCode) +{ + SetLastError(dwErrCode); + return false; +} + +static bool GetInfo_BufferCheck(void * pvFileInfo, DWORD cbFileInfo, DWORD cbData, LPDWORD pcbLengthNeeded) +{ + // Give the length needed to store the info + if(pcbLengthNeeded != NULL) + pcbLengthNeeded[0] = cbData; + + // Check for sufficient buffer + if(cbData > cbFileInfo) + return GetInfo_ReturnError(ERROR_INSUFFICIENT_BUFFER); + + // If the buffer size is sufficient, check for valid user buffer + if(pvFileInfo == NULL) + return GetInfo_ReturnError(ERROR_INVALID_PARAMETER); + + // Buffers and sizes are OK, we are ready to proceed file copying + return true; +} + +static bool GetInfo(void * pvFileInfo, DWORD cbFileInfo, const void * pvData, DWORD cbData, LPDWORD pcbLengthNeeded) +{ + // Verify buffer pointer and buffer size + if(!GetInfo_BufferCheck(pvFileInfo, cbFileInfo, cbData, pcbLengthNeeded)) + return false; + + // Copy the data to the caller-supplied buffer + memcpy(pvFileInfo, pvData, cbData); + return true; +} + +static bool GetInfo_Allocated(void * pvFileInfo, DWORD cbFileInfo, void * pvData, DWORD cbData, LPDWORD pcbLengthNeeded) +{ + bool bResult; + + // Verify buffer pointer and buffer size + if((bResult = GetInfo_BufferCheck(pvFileInfo, cbFileInfo, cbData, pcbLengthNeeded)) != false) + memcpy(pvFileInfo, pvData, cbData); + + // Copy the data to the user buffer + STORM_FREE(pvData); + return bResult; +} + +static bool GetInfo_TablePointer(void * pvFileInfo, DWORD cbFileInfo, void * pvTablePointer, SFileInfoClass InfoClass, LPDWORD pcbLengthNeeded) +{ + // Verify buffer pointer and buffer size + if(!GetInfo_BufferCheck(pvFileInfo, cbFileInfo, sizeof(void *), pcbLengthNeeded)) + { + SFileFreeFileInfo(pvTablePointer, InfoClass); + return false; + } + + // The user buffer receives pointer to the table. + // When done, the caller needs to call SFileFreeFileInfo on it + *(void **)pvFileInfo = pvTablePointer; + return true; +} + +static bool GetInfo_ReadFromFile(void * pvFileInfo, DWORD cbFileInfo, TFileStream * pStream, ULONGLONG ByteOffset, DWORD cbData, LPDWORD pcbLengthNeeded) +{ + // Verify buffer pointer and buffer size + if(!GetInfo_BufferCheck(pvFileInfo, cbFileInfo, cbData, pcbLengthNeeded)) + return false; + + return FileStream_Read(pStream, &ByteOffset, pvFileInfo, cbData); +} + +static bool GetInfo_FileEntry(void * pvFileInfo, DWORD cbFileInfo, TFileEntry * pFileEntry, LPDWORD pcbLengthNeeded) +{ + LPBYTE pbFileInfo = (LPBYTE)pvFileInfo; + DWORD cbSrcFileInfo = sizeof(TFileEntry); + DWORD cbFileName = 1; + + // The file name belongs to the file entry + if(pFileEntry->szFileName) + cbFileName = (DWORD)strlen(pFileEntry->szFileName) + 1; + cbSrcFileInfo += cbFileName; + + // Verify buffer pointer and buffer size + if(!GetInfo_BufferCheck(pvFileInfo, cbFileInfo, cbSrcFileInfo, pcbLengthNeeded)) + return false; + + // Copy the file entry + memcpy(pbFileInfo, pFileEntry, sizeof(TFileEntry)); + pbFileInfo += sizeof(TFileEntry); + pbFileInfo[0] = 0; + + // Copy the file name + if(pFileEntry->szFileName) + memcpy(pbFileInfo, pFileEntry->szFileName, cbFileName); + return true; +} + +static bool GetInfo_PatchChain(TMPQFile * hf, void * pvFileInfo, DWORD cbFileInfo, LPDWORD pcbLengthNeeded) { TMPQFile * hfTemp; - TCHAR * szFileInfo = (TCHAR *)pvFileInfo; + LPCTSTR szPatchName; + LPTSTR szFileInfo = (LPTSTR)pvFileInfo; size_t cchCharsNeeded = 1; - size_t cchFileInfo = (cbFileInfo / sizeof(TCHAR)); size_t nLength; - // Patch chain is only supported on MPQ files. + // Patch chain is only supported on MPQ files. Local files are not supported. if(hf->pStream != NULL) - { - SetLastError(ERROR_INVALID_PARAMETER); - return false; - } + return GetInfo_ReturnError(ERROR_INVALID_PARAMETER); // Calculate the necessary length of the multi-string for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatch) cchCharsNeeded += _tcslen(FileStream_GetFileName(hfTemp->ha->pStream)) + 1; - // Give the caller the needed length - if(pcbLengthNeeded != NULL) - pcbLengthNeeded[0] = (DWORD)(cchCharsNeeded * sizeof(TCHAR)); + // Verify whether the caller gave us valid buffer with enough size + if(!GetInfo_BufferCheck(pvFileInfo, cbFileInfo, (DWORD)(cchCharsNeeded * sizeof(TCHAR)), pcbLengthNeeded)) + return false; - // If the caller gave both buffer pointer and data length, - // try to copy the patch chain - if(szFileInfo != NULL && cchFileInfo != 0) + // Copy each patch name + for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatch) { - // If there is enough space in the buffer, copy the patch chain - if(cchCharsNeeded > cchFileInfo) - { - SetLastError(ERROR_INSUFFICIENT_BUFFER); - return false; - } - - // Copy each patch - for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatch) - { - // Get the file name and its length - const TCHAR * szFileName = FileStream_GetFileName(hfTemp->ha->pStream); - nLength = _tcslen(szFileName) + 1; - - // Copy the file name - memcpy(szFileInfo, szFileName, nLength * sizeof(TCHAR)); - szFileInfo += nLength; - } + // Get the file name and its length + szPatchName = FileStream_GetFileName(hfTemp->ha->pStream); + nLength = _tcslen(szPatchName) + 1; - // Make it multi-string - szFileInfo[0] = 0; + // Copy the file name + memcpy(szFileInfo, szPatchName, nLength * sizeof(TCHAR)); + szFileInfo += nLength; } + // Make it multi-string + szFileInfo[0] = 0; return true; } @@ -131,7 +186,7 @@ static bool GetFilePatchChain(TMPQFile * hf, void * pvFileInfo, DWORD cbFileInfo // cbFileInfo - Size of the buffer pointed by pvFileInfo // pcbLengthNeeded - Receives number of bytes necessary to store the information -bool STORMAPI SFileGetFileInfo( +bool WINAPI SFileGetFileInfo( HANDLE hMpqOrFile, SFileInfoClass InfoClass, void * pvFileInfo, @@ -139,728 +194,256 @@ bool STORMAPI SFileGetFileInfo( LPDWORD pcbLengthNeeded) { MPQ_SIGNATURE_INFO SignatureInfo; + const TCHAR * szSrcFileInfo; TMPQArchive * ha = NULL; TFileEntry * pFileEntry = NULL; + TMPQHeader * pHeader = NULL; ULONGLONG Int64Value = 0; - ULONGLONG ByteOffset = 0; TMPQFile * hf = NULL; void * pvSrcFileInfo = NULL; DWORD cbSrcFileInfo = 0; DWORD dwInt32Value = 0; - int nInfoType = SFILE_INFO_TYPE_INVALID_HANDLE; - int nError = ERROR_SUCCESS; + // Validate archive/file handle + if((int)InfoClass <= (int)SFileMpqFlags) + { + if((ha = IsValidMpqHandle(hMpqOrFile)) == NULL) + return GetInfo_ReturnError(ERROR_INVALID_HANDLE); + pHeader = ha->pHeader; + } + else + { + if((hf = IsValidFileHandle(hMpqOrFile)) == NULL) + return GetInfo_ReturnError(ERROR_INVALID_HANDLE); + pFileEntry = hf->pFileEntry; + } + + // Return info-class-specific data switch(InfoClass) { case SFileMpqFileName: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - pvSrcFileInfo = (void *)FileStream_GetFileName(ha->pStream); - cbSrcFileInfo = (DWORD)(_tcslen((TCHAR *)pvSrcFileInfo) + 1) * sizeof(TCHAR); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + szSrcFileInfo = FileStream_GetFileName(ha->pStream); + cbSrcFileInfo = (DWORD)((_tcslen(szSrcFileInfo) + 1) * sizeof(TCHAR)); + return GetInfo(pvFileInfo, cbFileInfo, szSrcFileInfo, cbSrcFileInfo, pcbLengthNeeded); case SFileMpqStreamBitmap: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - return FileStream_GetBitmap(ha->pStream, pvFileInfo, cbFileInfo, pcbLengthNeeded); - break; + return FileStream_GetBitmap(ha->pStream, pvFileInfo, cbFileInfo, pcbLengthNeeded); case SFileMpqUserDataOffset: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - nInfoType = SFILE_INFO_TYPE_NOT_FOUND; - if(ha->pUserData != NULL) - { - pvSrcFileInfo = &ha->UserDataPos; - cbSrcFileInfo = sizeof(ULONGLONG); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &ha->UserDataPos, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqUserDataHeader: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - nInfoType = SFILE_INFO_TYPE_NOT_FOUND; - if(ha->pUserData != NULL) - { - ByteOffset = ha->UserDataPos; - cbSrcFileInfo = sizeof(TMPQUserData); - nInfoType = SFILE_INFO_TYPE_READ_FROM_FILE; - } - } - break; + return GetInfo_ReadFromFile(pvFileInfo, cbFileInfo, ha->pStream, ha->UserDataPos, sizeof(TMPQUserData), pcbLengthNeeded); case SFileMpqUserData: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - nInfoType = SFILE_INFO_TYPE_NOT_FOUND; - if(ha->pUserData != NULL) - { - ByteOffset = ha->UserDataPos + sizeof(TMPQUserData); - cbSrcFileInfo = ha->pUserData->dwHeaderOffs - sizeof(TMPQUserData); - nInfoType = SFILE_INFO_TYPE_READ_FROM_FILE; - } - } - break; + return GetInfo_ReadFromFile(pvFileInfo, cbFileInfo, ha->pStream, ha->UserDataPos + sizeof(TMPQUserData), ha->pUserData->dwHeaderOffs - sizeof(TMPQUserData), pcbLengthNeeded); case SFileMpqHeaderOffset: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - pvSrcFileInfo = &ha->MpqPos; - cbSrcFileInfo = sizeof(ULONGLONG); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &ha->MpqPos, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqHeaderSize: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - pvSrcFileInfo = &ha->pHeader->dwHeaderSize; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->dwHeaderSize, sizeof(DWORD), pcbLengthNeeded); case SFileMpqHeader: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - ByteOffset = ha->MpqPos; - cbSrcFileInfo = ha->pHeader->dwHeaderSize; - nInfoType = SFILE_INFO_TYPE_READ_FROM_FILE; - } - break; + return GetInfo_ReadFromFile(pvFileInfo, cbFileInfo, ha->pStream, ha->MpqPos, pHeader->dwHeaderSize, pcbLengthNeeded); case SFileMpqHetTableOffset: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - pvSrcFileInfo = &ha->pHeader->HetTablePos64; - cbSrcFileInfo = sizeof(ULONGLONG); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->HetTablePos64, sizeof(ULONGLONG), pcbLengthNeeded); -#ifdef FULL case SFileMpqHetTableSize: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - pvSrcFileInfo = &ha->pHeader->HetTableSize64; - cbSrcFileInfo = sizeof(ULONGLONG); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->HetTableSize64, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqHetHeader: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - nInfoType = SFILE_INFO_TYPE_NOT_FOUND; - pvSrcFileInfo = LoadExtTable(ha, ha->pHeader->HetTablePos64, (size_t)ha->pHeader->HetTableSize64, HET_TABLE_SIGNATURE, MPQ_KEY_HASH_TABLE); - if(pvSrcFileInfo != NULL) - { - cbSrcFileInfo = sizeof(TMPQHetHeader); - nInfoType = SFILE_INFO_TYPE_ALLOCATED; - } - } - break; + pvSrcFileInfo = LoadExtTable(ha, pHeader->HetTablePos64, (size_t)pHeader->HetTableSize64, HET_TABLE_SIGNATURE, MPQ_KEY_HASH_TABLE); + if(pvSrcFileInfo == NULL) + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); + return GetInfo_Allocated(pvFileInfo, cbFileInfo, pvSrcFileInfo, sizeof(TMPQHetHeader), pcbLengthNeeded); case SFileMpqHetTable: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - nInfoType = SFILE_INFO_TYPE_NOT_FOUND; - pvSrcFileInfo = LoadHetTable(ha); - if(pvSrcFileInfo != NULL) - { - cbSrcFileInfo = sizeof(void *); - nInfoType = SFILE_INFO_TYPE_TABLE_POINTER; - } - } - break; + if((pvSrcFileInfo = LoadHetTable(ha)) == NULL) + return GetInfo_ReturnError(ERROR_NOT_ENOUGH_MEMORY); + return GetInfo_TablePointer(pvFileInfo, cbFileInfo, pvSrcFileInfo, InfoClass, pcbLengthNeeded); case SFileMpqBetTableOffset: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - pvSrcFileInfo = &ha->pHeader->BetTablePos64; - cbSrcFileInfo = sizeof(ULONGLONG); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->BetTablePos64, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqBetTableSize: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - pvSrcFileInfo = &ha->pHeader->BetTableSize64; - cbSrcFileInfo = sizeof(ULONGLONG); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->BetTableSize64, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqBetHeader: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - nInfoType = SFILE_INFO_TYPE_NOT_FOUND; - pvSrcFileInfo = LoadExtTable(ha, ha->pHeader->BetTablePos64, (size_t)ha->pHeader->BetTableSize64, BET_TABLE_SIGNATURE, MPQ_KEY_BLOCK_TABLE); - if(pvSrcFileInfo != NULL) - { - // It is allowed for the caller to only require BET header. - cbSrcFileInfo = sizeof(TMPQBetHeader) + ((TMPQBetHeader *)pvSrcFileInfo)->dwFlagCount * sizeof(DWORD); - if(cbFileInfo == sizeof(TMPQBetHeader)) - cbSrcFileInfo = sizeof(TMPQBetHeader); - nInfoType = SFILE_INFO_TYPE_ALLOCATED; - } - } - break; + + // Retrieve the table and its size + pvSrcFileInfo = LoadExtTable(ha, pHeader->BetTablePos64, (size_t)pHeader->BetTableSize64, BET_TABLE_SIGNATURE, MPQ_KEY_BLOCK_TABLE); + if(pvSrcFileInfo == NULL) + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); + cbSrcFileInfo = sizeof(TMPQBetHeader) + ((TMPQBetHeader *)pvSrcFileInfo)->dwFlagCount * sizeof(DWORD); + + // It is allowed for the caller to only require BET header + if(cbFileInfo == sizeof(TMPQBetHeader)) + cbSrcFileInfo = sizeof(TMPQBetHeader); + return GetInfo_Allocated(pvFileInfo, cbFileInfo, pvSrcFileInfo, cbSrcFileInfo, pcbLengthNeeded); case SFileMpqBetTable: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - nInfoType = SFILE_INFO_TYPE_NOT_FOUND; - pvSrcFileInfo = LoadBetTable(ha); - if(pvSrcFileInfo != NULL) - { - cbSrcFileInfo = sizeof(void *); - nInfoType = SFILE_INFO_TYPE_TABLE_POINTER; - } - } - break; -#endif + if((pvSrcFileInfo = LoadBetTable(ha)) == NULL) + return GetInfo_ReturnError(ERROR_NOT_ENOUGH_MEMORY); + return GetInfo_TablePointer(pvFileInfo, cbFileInfo, pvSrcFileInfo, InfoClass, pcbLengthNeeded); + case SFileMpqHashTableOffset: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - Int64Value = MAKE_OFFSET64(ha->pHeader->wHashTablePosHi, ha->pHeader->dwHashTablePos); - pvSrcFileInfo = &Int64Value; - cbSrcFileInfo = sizeof(ULONGLONG); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + Int64Value = MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos); + return GetInfo(pvFileInfo, cbFileInfo, &Int64Value, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqHashTableSize64: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - pvSrcFileInfo = &ha->pHeader->HashTableSize64; - cbSrcFileInfo = sizeof(ULONGLONG); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->HashTableSize64, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqHashTableSize: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - pvSrcFileInfo = &ha->pHeader->dwHashTableSize; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->dwHashTableSize, sizeof(DWORD), pcbLengthNeeded); case SFileMpqHashTable: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL && ha->pHashTable != NULL) - { - pvSrcFileInfo = ha->pHashTable; - cbSrcFileInfo = ha->pHeader->dwHashTableSize * sizeof(TMPQHash); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + cbSrcFileInfo = pHeader->dwHashTableSize * sizeof(TMPQHash); + return GetInfo(pvFileInfo, cbFileInfo, ha->pHashTable, cbSrcFileInfo, pcbLengthNeeded); case SFileMpqBlockTableOffset: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - Int64Value = MAKE_OFFSET64(ha->pHeader->wBlockTablePosHi, ha->pHeader->dwBlockTablePos); - pvSrcFileInfo = &Int64Value; - cbSrcFileInfo = sizeof(ULONGLONG); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + Int64Value = MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); + return GetInfo(pvFileInfo, cbFileInfo, &Int64Value, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqBlockTableSize64: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - pvSrcFileInfo = &ha->pHeader->BlockTableSize64; - cbSrcFileInfo = sizeof(ULONGLONG); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->BlockTableSize64, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqBlockTableSize: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - pvSrcFileInfo = &ha->pHeader->dwBlockTableSize; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->dwBlockTableSize, sizeof(DWORD), pcbLengthNeeded); case SFileMpqBlockTable: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - nInfoType = SFILE_INFO_TYPE_NOT_FOUND; - if(MAKE_OFFSET64(ha->pHeader->wBlockTablePosHi, ha->pHeader->dwBlockTablePos) < ha->FileSize) - { - cbSrcFileInfo = ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock); - if(cbFileInfo >= cbSrcFileInfo) - pvSrcFileInfo = LoadBlockTable(ha, true); - nInfoType = SFILE_INFO_TYPE_ALLOCATED; - } - } - break; + if(MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos) >= ha->FileSize) + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); + cbSrcFileInfo = pHeader->dwBlockTableSize * sizeof(TMPQBlock); + pvSrcFileInfo = LoadBlockTable(ha, true); + return GetInfo_Allocated(pvFileInfo, cbFileInfo, pvSrcFileInfo, cbSrcFileInfo, pcbLengthNeeded); case SFileMpqHiBlockTableOffset: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - pvSrcFileInfo = &ha->pHeader->HiBlockTablePos64; - cbSrcFileInfo = sizeof(ULONGLONG); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->HiBlockTablePos64, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqHiBlockTableSize64: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - pvSrcFileInfo = &ha->pHeader->HiBlockTableSize64; - cbSrcFileInfo = sizeof(ULONGLONG); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->HiBlockTableSize64, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqHiBlockTable: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - nInfoType = SFILE_INFO_TYPE_NOT_FOUND; - if(ha->pHeader->HiBlockTablePos64 && ha->pHeader->HiBlockTableSize64) - { - assert(false); - } - } - break; + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); -#ifdef FULL case SFileMpqSignatures: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL && QueryMpqSignatureInfo(ha, &SignatureInfo)) - { - pvSrcFileInfo = &SignatureInfo.SignatureTypes; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + if(!QueryMpqSignatureInfo(ha, &SignatureInfo)) + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); + return GetInfo(pvFileInfo, cbFileInfo, &SignatureInfo.SignatureTypes, sizeof(DWORD), pcbLengthNeeded); case SFileMpqStrongSignatureOffset: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - nInfoType = SFILE_INFO_TYPE_NOT_FOUND; - if(QueryMpqSignatureInfo(ha, &SignatureInfo) && (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG)) - { - pvSrcFileInfo = &SignatureInfo.EndMpqData; - cbSrcFileInfo = sizeof(ULONGLONG); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - } - break; + if(QueryMpqSignatureInfo(ha, &SignatureInfo) == false || (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG) == 0) + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); + return GetInfo(pvFileInfo, cbFileInfo, &SignatureInfo.EndMpqData, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqStrongSignatureSize: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - nInfoType = SFILE_INFO_TYPE_NOT_FOUND; - if(QueryMpqSignatureInfo(ha, &SignatureInfo) && (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG)) - { - dwInt32Value = MPQ_STRONG_SIGNATURE_SIZE + 4; - pvSrcFileInfo = &dwInt32Value; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - } - break; + if(QueryMpqSignatureInfo(ha, &SignatureInfo) == false || (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG) == 0) + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); + dwInt32Value = MPQ_STRONG_SIGNATURE_SIZE + 4; + return GetInfo(pvFileInfo, cbFileInfo, &dwInt32Value, sizeof(DWORD), pcbLengthNeeded); case SFileMpqStrongSignature: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - nInfoType = SFILE_INFO_TYPE_NOT_FOUND; - if(QueryMpqSignatureInfo(ha, &SignatureInfo) && (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG)) - { - pvSrcFileInfo = SignatureInfo.Signature; - cbSrcFileInfo = MPQ_STRONG_SIGNATURE_SIZE + 4; - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - } - break; -#endif + if(QueryMpqSignatureInfo(ha, &SignatureInfo) == false || (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG) == 0) + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); + return GetInfo(pvFileInfo, cbFileInfo, SignatureInfo.Signature, MPQ_STRONG_SIGNATURE_SIZE + 4, pcbLengthNeeded); case SFileMpqArchiveSize64: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - pvSrcFileInfo = &ha->pHeader->ArchiveSize64; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->ArchiveSize64, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqArchiveSize: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - pvSrcFileInfo = &ha->pHeader->dwArchiveSize; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->dwArchiveSize, sizeof(DWORD), pcbLengthNeeded); case SFileMpqMaxFileCount: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - pvSrcFileInfo = &ha->dwMaxFileCount; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &ha->dwMaxFileCount, sizeof(DWORD), pcbLengthNeeded); case SFileMpqFileTableSize: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - pvSrcFileInfo = &ha->dwFileTableSize; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &ha->dwFileTableSize, sizeof(DWORD), pcbLengthNeeded); case SFileMpqSectorSize: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - pvSrcFileInfo = &ha->dwSectorSize; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &ha->dwSectorSize, sizeof(DWORD), pcbLengthNeeded); case SFileMpqNumberOfFiles: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - pvSrcFileInfo = &dwInt32Value; - cbSrcFileInfo = sizeof(DWORD); - dwInt32Value = GetMpqFileCount(ha); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + dwInt32Value = GetMpqFileCount(ha); + return GetInfo(pvFileInfo, cbFileInfo, &dwInt32Value, sizeof(DWORD), pcbLengthNeeded); case SFileMpqRawChunkSize: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - nInfoType = SFILE_INFO_TYPE_NOT_FOUND; - if(ha->pHeader->dwRawChunkSize != 0) - { - pvSrcFileInfo = &ha->pHeader->dwRawChunkSize; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - } - break; + if(pHeader->dwRawChunkSize == 0) + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); + return GetInfo(pvFileInfo, cbFileInfo, &pHeader->dwRawChunkSize, sizeof(DWORD), pcbLengthNeeded); case SFileMpqStreamFlags: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - FileStream_GetFlags(ha->pStream, &dwInt32Value); - pvSrcFileInfo = &dwInt32Value; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + FileStream_GetFlags(ha->pStream, &dwInt32Value); + return GetInfo(pvFileInfo, cbFileInfo, &dwInt32Value, sizeof(DWORD), pcbLengthNeeded); case SFileMpqFlags: - ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) - { - dwInt32Value = ha->dwFlags; - pvSrcFileInfo = &dwInt32Value; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &ha->dwFlags, sizeof(DWORD), pcbLengthNeeded); case SFileInfoPatchChain: - hf = IsValidFileHandle(hMpqOrFile); - if(hf != NULL) - return GetFilePatchChain(hf, pvFileInfo, cbFileInfo, pcbLengthNeeded); - break; + return GetInfo_PatchChain(hf, pvFileInfo, cbFileInfo, pcbLengthNeeded); case SFileInfoFileEntry: - hf = IsValidFileHandle(hMpqOrFile); - if(hf != NULL && hf->pFileEntry != NULL) - { - pvSrcFileInfo = pFileEntry = hf->pFileEntry; - cbSrcFileInfo = sizeof(TFileEntry); - if(pFileEntry->szFileName != NULL) - cbSrcFileInfo += (DWORD)strlen(pFileEntry->szFileName) + 1; - nInfoType = SFILE_INFO_TYPE_FILE_ENTRY; - } - break; + if(pFileEntry == NULL) + return GetInfo_ReturnError(ERROR_FILE_NOT_FOUND); + return GetInfo_FileEntry(pvFileInfo, cbFileInfo, pFileEntry, pcbLengthNeeded); case SFileInfoHashEntry: - hf = IsValidFileHandle(hMpqOrFile); - if(hf != NULL && hf->pHashEntry != NULL) - { - pvSrcFileInfo = hf->pHashEntry; - cbSrcFileInfo = sizeof(TMPQHash); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, hf->pHashEntry, sizeof(TMPQHash), pcbLengthNeeded); case SFileInfoHashIndex: - hf = IsValidFileHandle(hMpqOrFile); - if(hf != NULL && hf->pHashEntry != NULL) - { - pvSrcFileInfo = &hf->dwHashIndex; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &hf->dwHashIndex, sizeof(DWORD), pcbLengthNeeded); case SFileInfoNameHash1: - hf = IsValidFileHandle(hMpqOrFile); - if(hf != NULL && hf->pHashEntry != NULL) - { - dwInt32Value = hf->pHashEntry->dwName1; - pvSrcFileInfo = &dwInt32Value; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &hf->pHashEntry->dwName1, sizeof(DWORD), pcbLengthNeeded); case SFileInfoNameHash2: - hf = IsValidFileHandle(hMpqOrFile); - if(hf != NULL && hf->pHashEntry != NULL) - { - dwInt32Value = hf->pHashEntry->dwName2; - pvSrcFileInfo = &dwInt32Value; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &hf->pHashEntry->dwName2, sizeof(DWORD), pcbLengthNeeded); case SFileInfoNameHash3: - hf = IsValidFileHandle(hMpqOrFile); - if(hf != NULL && hf->pFileEntry != NULL) - { - pvSrcFileInfo = &hf->pFileEntry->FileNameHash; - cbSrcFileInfo = sizeof(ULONGLONG); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &pFileEntry->FileNameHash, sizeof(ULONGLONG), pcbLengthNeeded); case SFileInfoLocale: - hf = IsValidFileHandle(hMpqOrFile); - if(hf != NULL && hf->pHashEntry != NULL) - { - dwInt32Value = hf->pHashEntry->lcLocale; - pvSrcFileInfo = &dwInt32Value; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + dwInt32Value = hf->pHashEntry->lcLocale; + return GetInfo(pvFileInfo, cbFileInfo, &dwInt32Value, sizeof(DWORD), pcbLengthNeeded); case SFileInfoFileIndex: - hf = IsValidFileHandle(hMpqOrFile); - if(hf != NULL && hf->ha != NULL && hf->pFileEntry != NULL) - { - dwInt32Value = (DWORD)(hf->pFileEntry - hf->ha->pFileTable); - pvSrcFileInfo = &dwInt32Value; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + dwInt32Value = (DWORD)(pFileEntry - hf->ha->pFileTable); + return GetInfo(pvFileInfo, cbFileInfo, &dwInt32Value, sizeof(DWORD), pcbLengthNeeded); case SFileInfoByteOffset: - hf = IsValidFileHandle(hMpqOrFile); - if(hf != NULL && hf->pFileEntry != NULL) - { - pvSrcFileInfo = &hf->pFileEntry->ByteOffset; - cbSrcFileInfo = sizeof(ULONGLONG); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &pFileEntry->ByteOffset, sizeof(ULONGLONG), pcbLengthNeeded); case SFileInfoFileTime: - hf = IsValidFileHandle(hMpqOrFile); - if(hf != NULL && hf->pFileEntry != NULL) - { - pvSrcFileInfo = &hf->pFileEntry->FileTime; - cbSrcFileInfo = sizeof(ULONGLONG); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &pFileEntry->FileTime, sizeof(ULONGLONG), pcbLengthNeeded); case SFileInfoFileSize: - hf = IsValidFileHandle(hMpqOrFile); - if(hf != NULL && hf->pFileEntry != NULL) - { - pvSrcFileInfo = &hf->pFileEntry->dwFileSize; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &pFileEntry->dwFileSize, sizeof(DWORD), pcbLengthNeeded); case SFileInfoCompressedSize: - hf = IsValidFileHandle(hMpqOrFile); - if(hf != NULL && hf->pFileEntry != NULL) - { - pvSrcFileInfo = &hf->pFileEntry->dwCmpSize; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &pFileEntry->dwCmpSize, sizeof(DWORD), pcbLengthNeeded); case SFileInfoFlags: - hf = IsValidFileHandle(hMpqOrFile); - if(hf != NULL && hf->pFileEntry != NULL) - { - pvSrcFileInfo = &hf->pFileEntry->dwFlags; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &pFileEntry->dwFlags, sizeof(DWORD), pcbLengthNeeded); case SFileInfoEncryptionKey: - hf = IsValidFileHandle(hMpqOrFile); - if(hf != NULL) - { - pvSrcFileInfo = &hf->dwFileKey; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + return GetInfo(pvFileInfo, cbFileInfo, &hf->dwFileKey, sizeof(DWORD), pcbLengthNeeded); case SFileInfoEncryptionKeyRaw: - hf = IsValidFileHandle(hMpqOrFile); - if(hf != NULL && hf->pFileEntry != NULL) - { - dwInt32Value = hf->dwFileKey; - if(hf->pFileEntry->dwFlags & MPQ_FILE_FIX_KEY) - dwInt32Value = (dwInt32Value ^ hf->pFileEntry->dwFileSize) - (DWORD)hf->MpqFilePos; - pvSrcFileInfo = &dwInt32Value; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; + dwInt32Value = hf->dwFileKey; + if(pFileEntry->dwFlags & MPQ_FILE_FIX_KEY) + dwInt32Value = (dwInt32Value ^ pFileEntry->dwFileSize) - (DWORD)hf->MpqFilePos; + return GetInfo(pvFileInfo, cbFileInfo, &dwInt32Value, sizeof(DWORD), pcbLengthNeeded); case SFileInfoCRC32: - hf = IsValidFileHandle(hMpqOrFile); - if(hf != NULL && hf->pFileEntry != NULL) - { - dwInt32Value = hf->pFileEntry->dwCrc32; - pvSrcFileInfo = &dwInt32Value; - cbSrcFileInfo = sizeof(DWORD); - nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; - } - break; - - default: // Invalid info class - SetLastError(ERROR_INVALID_PARAMETER); - return false; - } - - // If we validated the handle and info class, give as much info as possible - if(nInfoType >= SFILE_INFO_TYPE_DIRECT_POINTER) - { - // Give the length needed, if wanted - if(pcbLengthNeeded != NULL) - pcbLengthNeeded[0] = cbSrcFileInfo; - - // If the caller entered an output buffer, the output size must also be entered - if(pvFileInfo != NULL && cbFileInfo != 0) - { - // Check if there is enough space in the output buffer - if(cbSrcFileInfo <= cbFileInfo) - { - switch(nInfoType) - { - case SFILE_INFO_TYPE_DIRECT_POINTER: - case SFILE_INFO_TYPE_ALLOCATED: - assert(pvSrcFileInfo != NULL); - memcpy(pvFileInfo, pvSrcFileInfo, cbSrcFileInfo); - break; - - case SFILE_INFO_TYPE_READ_FROM_FILE: - if(!FileStream_Read(ha->pStream, &ByteOffset, pvFileInfo, cbSrcFileInfo)) - nError = GetLastError(); - break; - - case SFILE_INFO_TYPE_TABLE_POINTER: - assert(pvSrcFileInfo != NULL); - *(void **)pvFileInfo = pvSrcFileInfo; - pvSrcFileInfo = NULL; - break; - - case SFILE_INFO_TYPE_FILE_ENTRY: - assert(pFileEntry != NULL); - ConvertFileEntryToSelfRelative((TFileEntry *)pvFileInfo, pFileEntry); - break; - } - } - else - { - nError = ERROR_INSUFFICIENT_BUFFER; - } - } - - // Free the file info if needed - if(nInfoType == SFILE_INFO_TYPE_ALLOCATED && pvSrcFileInfo != NULL) - STORM_FREE(pvSrcFileInfo); -#ifdef FULL - if(nInfoType == SFILE_INFO_TYPE_TABLE_POINTER && pvSrcFileInfo != NULL) - SFileFreeFileInfo(pvSrcFileInfo, InfoClass); -#endif - } - else - { - // Handle error cases - if(nInfoType == SFILE_INFO_TYPE_INVALID_HANDLE) - nError = ERROR_INVALID_HANDLE; - if(nInfoType == SFILE_INFO_TYPE_NOT_FOUND) - nError = ERROR_FILE_NOT_FOUND; + return GetInfo(pvFileInfo, cbFileInfo, &hf->pFileEntry->dwCrc32, sizeof(DWORD), pcbLengthNeeded); } - // Set the last error value, if needed - if(nError != ERROR_SUCCESS) - SetLastError(nError); - return (nError == ERROR_SUCCESS); + // Invalid info class + return GetInfo_ReturnError(ERROR_INVALID_PARAMETER); } -#ifdef FULL -bool STORMAPI SFileFreeFileInfo(void * pvFileInfo, SFileInfoClass InfoClass) +bool WINAPI SFileFreeFileInfo(void * pvFileInfo, SFileInfoClass InfoClass) { switch(InfoClass) { @@ -879,7 +462,6 @@ bool STORMAPI SFileFreeFileInfo(void * pvFileInfo, SFileInfoClass InfoClass) SetLastError(ERROR_INVALID_PARAMETER); return false; } -#endif //----------------------------------------------------------------------------- // Tries to retrieve the file name @@ -916,6 +498,18 @@ static TFileHeader2Ext data2ext[] = {0x47585053, 0xFFFFFFFF, 0x00000000, 0x00000000, "bls"}, // WoW pixel shaders {0xE0FFD8FF, 0xFFFFFFFF, 0x00000000, 0x00000000, "jpg"}, // JPEG image {0x503B4449, 0xFFFFFFFF, 0x3B4C5857, 0xFFFFFFFF, "slk"}, // SLK file (usually starts with "ID;PWXL;N;E") + {0x61754C1B, 0xFFFFFFFF, 0x00000000, 0x00000000, "lua"}, // Compiled LUA files + {0x20534444, 0xFFFFFFFF, 0x00000000, 0x00000000, "dds"}, // DDS textures + {0x43614C66, 0xFFFFFFFF, 0x00000000, 0x00000000, "flac"}, // FLAC sound files + {0x0000FBFF, 0x0000FFFF, 0x00000000, 0x00000000, "mp3"}, // MP3 sound files + {0x0000F3FF, 0x0000FFFF, 0x00000000, 0x00000000, "mp3"}, // MP3 sound files + {0x0000F2FF, 0x0000FFFF, 0x00000000, 0x00000000, "mp3"}, // MP3 sound files + {0x00334449, 0x00FFFFFF, 0x00000000, 0x00000000, "mp3"}, // MP3 sound files + {0x57334D48, 0xFFFFFFFF, 0x00000000, 0x00000000, "w3x"}, // Warcraft III map files, can also be w3m + {0x6F643357, 0xFFFFFFFF, 0x00000000, 0x00000000, "doo"}, // Warcraft III doodad files + {0x21453357, 0xFFFFFFFF, 0x00000000, 0x00000000, "w3e"}, // Warcraft III environment files + {0x5733504D, 0xFFFFFFFF, 0x00000000, 0x00000000, "wpm"}, // Warcraft III pathing map files + {0x21475457, 0xFFFFFFFF, 0x00000000, 0x00000000, "wtg"}, // Warcraft III trigger files {0x00000000, 0x00000000, 0x00000000, 0x00000000, "xxx"}, // Default extension {0, 0, 0, 0, NULL} // Terminator }; @@ -926,6 +520,7 @@ static int CreatePseudoFileName(HANDLE hFile, TFileEntry * pFileEntry, char * sz DWORD FirstBytes[2] = {0, 0}; // The first 4 bytes of the file DWORD dwBytesRead = 0; DWORD dwFilePos; // Saved file position + char szPseudoName[20]; // Read the first 2 DWORDs bytes from the file dwFilePos = SFileSetFilePointer(hFile, 0, NULL, FILE_CURRENT); @@ -944,10 +539,8 @@ static int CreatePseudoFileName(HANDLE hFile, TFileEntry * pFileEntry, char * sz if((FirstBytes[0] & data2ext[i].dwOffset00Mask) == data2ext[i].dwOffset00Data && (FirstBytes[1] & data2ext[i].dwOffset04Mask) == data2ext[i].dwOffset04Data) { - char szPseudoName[20] = ""; - // Format the pseudo-name - sprintf(szPseudoName, "File%08u.%s", (unsigned int)(pFileEntry - hf->ha->pFileTable), data2ext[i].szExt); + StringCreatePseudoFileName(szPseudoName, _countof(szPseudoName), (unsigned int)(pFileEntry - hf->ha->pFileTable), data2ext[i].szExt); // Save the pseudo-name in the file entry as well AllocateFileName(hf->ha, pFileEntry, szPseudoName); @@ -963,7 +556,7 @@ static int CreatePseudoFileName(HANDLE hFile, TFileEntry * pFileEntry, char * sz return ERROR_CAN_NOT_COMPLETE; } -bool STORMAPI SFileGetFileName(HANDLE hFile, char * szFileName) +bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName) { TMPQFile * hf = (TMPQFile *)hFile; // MPQ File handle int nError = ERROR_INVALID_HANDLE; diff --git a/3rdParty/StormLib/src/SFileOpenArchive.cpp b/3rdParty/StormLib/src/SFileOpenArchive.cpp index 493f5532e..e8ef64aa7 100644 --- a/3rdParty/StormLib/src/SFileOpenArchive.cpp +++ b/3rdParty/StormLib/src/SFileOpenArchive.cpp @@ -1,624 +1,657 @@ -/*****************************************************************************/ -/* SFileOpenArchive.cpp Copyright Ladislav Zezula 1999 */ -/* */ -/* Author : Ladislav Zezula */ -/* E-mail : ladik@zezula.net */ -/* WWW : www.zezula.net */ -/*---------------------------------------------------------------------------*/ -/* Archive functions of Storm.dll */ -/*---------------------------------------------------------------------------*/ -/* Date Ver Who Comment */ -/* -------- ---- --- ------- */ -/* xx.xx.xx 1.00 Lad The first version of SFileOpenArchive.cpp */ -/* 19.11.03 1.01 Dan Big endian handling */ -/*****************************************************************************/ - -#ifndef FULL -#include -#include -#endif - -#define __STORMLIB_SELF__ -#include "StormLib.h" -#include "StormCommon.h" - -#define HEADER_SEARCH_BUFFER_SIZE 0x1000 - -/*****************************************************************************/ -/* Local functions */ -/*****************************************************************************/ - -static bool IsAviFile(DWORD * HeaderData) -{ - DWORD DwordValue0 = BSWAP_INT32_UNSIGNED(HeaderData[0]); - DWORD DwordValue2 = BSWAP_INT32_UNSIGNED(HeaderData[2]); - DWORD DwordValue3 = BSWAP_INT32_UNSIGNED(HeaderData[3]); - - // Test for 'RIFF', 'AVI ' or 'LIST' - return (DwordValue0 == 0x46464952 && DwordValue2 == 0x20495641 && DwordValue3 == 0x5453494C); -} - -static bool IsWarcraft3Map(DWORD * HeaderData) -{ - DWORD DwordValue0 = BSWAP_INT32_UNSIGNED(HeaderData[0]); - DWORD DwordValue1 = BSWAP_INT32_UNSIGNED(HeaderData[1]); - - return (DwordValue0 == 0x57334D48 && DwordValue1 == 0x00000000); -} - -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 int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize) -{ - TMPQHeader * pHeader = ha->pHeader; - ULONGLONG ByteOffset; - - // 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; -} - - -/*****************************************************************************/ -/* Public functions */ -/*****************************************************************************/ - -//----------------------------------------------------------------------------- -// SFileGetLocale and SFileSetLocale -// Set the locale for all newly opened files - -LCID STORMAPI SFileGetLocale() -{ - return lcFileLocale; -} - -LCID STORMAPI SFileSetLocale(LCID lcNewLocale) -{ - lcFileLocale = lcNewLocale; - return 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 STORMAPI 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); - bool bIsWarcraft3Map = false; - int nError = 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; - -#ifndef FULL - std::string name = szMpqName; - std::replace(name.begin(), name.end(), '\\', '/'); - szMpqName = name.c_str(); -#endif - - // 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(nError == ERROR_SUCCESS) - { - FileStream_GetSize(pStream, &FileSize); - if(FileSize < MPQ_HEADER_SIZE_V1) - nError = ERROR_BAD_FORMAT; - } - - // Allocate the MPQhandle - if(nError == ERROR_SUCCESS) - { - if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; - } - - // Allocate buffer for searching MPQ header - if(nError == ERROR_SUCCESS) - { - pbHeaderBuffer = STORM_ALLOC(BYTE, HEADER_SEARCH_BUFFER_SIZE); - if(pbHeaderBuffer == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; - } - - // Find the position of MPQ header - if(nError == ERROR_SUCCESS) - { - ULONGLONG SearchOffset = 0; - ULONGLONG EndOfSearch = FileSize; - DWORD dwStrmFlags = 0; - DWORD dwHeaderSize; - DWORD dwHeaderID; - bool bSearchComplete = false; - - memset(ha, 0, sizeof(TMPQArchive)); - 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; - - // Find the offset of MPQ header within the file - while(bSearchComplete == false && SearchOffset < EndOfSearch) - { - // Always read at least 0x1000 bytes for performance. - // This is what Storm.dll (2002) does. - DWORD dwBytesAvailable = HEADER_SEARCH_BUFFER_SIZE; - DWORD dwInBufferOffset = 0; - - // Cut the bytes available, if needed - if((FileSize - SearchOffset) < HEADER_SEARCH_BUFFER_SIZE) - dwBytesAvailable = (DWORD)(FileSize - SearchOffset); - - // Read the eventual MPQ header - if(!FileStream_Read(ha->pStream, &SearchOffset, pbHeaderBuffer, dwBytesAvailable)) - { - nError = GetLastError(); - break; - } - - // There are AVI files from Warcraft III with 'MPQ' extension. - if(SearchOffset == 0) - { - if(IsAviFile((DWORD *)pbHeaderBuffer)) - { - nError = ERROR_AVI_FILE; - break; - } - - bIsWarcraft3Map = IsWarcraft3Map((DWORD *)pbHeaderBuffer); - } - - // Search the header buffer - while(dwInBufferOffset < dwBytesAvailable) - { - // 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(bIsWarcraft3Map == false && (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(SearchOffset, FileSize, ha->HeaderData); - if(pUserData != NULL) - { - // Fill the user data header - ha->UserDataPos = SearchOffset; - ha->pUserData = &ha->UserData; - memcpy(ha->pUserData, pUserData, sizeof(TMPQUserData)); - - // Continue searching from that position - SearchOffset += 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 == ID_MPQ && dwHeaderSize >= MPQ_HEADER_SIZE_V1) - { - // Now convert the header to version 4 - nError = ConvertMpqHeaderToFormat4(ha, SearchOffset, FileSize, dwFlags, bIsWarcraft3Map); - bSearchComplete = true; - break; - } - - // Check for MPK archives (Longwu Online - MPQ fork) - if(bIsWarcraft3Map == false && dwHeaderID == ID_MPK) - { - // Now convert the MPK header to MPQ Header version 4 - nError = 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) - { - nError = ERROR_NOT_SUPPORTED; - bSearchComplete = true; - break; - } - - // Move the pointers - SearchOffset += 0x200; - dwInBufferOffset += 0x200; - } - } - - // Did we identify one of the supported headers? - if(nError == ERROR_SUCCESS) - { - // Set the user data position to the MPQ header, if none - if(ha->pUserData == NULL) - ha->UserDataPos = SearchOffset; - - // Set the position of the MPQ header - ha->pHeader = (TMPQHeader *)ha->HeaderData; - ha->MpqPos = SearchOffset; - ha->FileSize = FileSize; - - // Sector size must be nonzero. - if(SearchOffset >= FileSize || ha->pHeader->wSectorSize == 0) - nError = ERROR_BAD_FORMAT; - } - } - - // Fix table positions according to format - if(nError == 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; - - // Remember whether whis is a map for Warcraft III - if(bIsWarcraft3Map) - ha->dwFlags |= MPQ_FLAG_WAR3_MAP; - - // 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 - nError = 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(nError == ERROR_SUCCESS) - { - nError = 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(nError == ERROR_SUCCESS) - { - nError = BuildFileTable(ha); - } - -#ifdef FULL - // Load the internal listfile and include it to the file table - if(nError == 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(nError == 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; - } - } -#endif - - // Remember whether the archive has weak signature. Only for MPQs format 1.0. - if(nError == 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(nError != ERROR_SUCCESS) - { - FileStream_Close(pStream); - FreeArchiveHandle(ha); - SetLastError(nError); - ha = NULL; - } - - // Free the header buffer - if(pbHeaderBuffer != NULL) - STORM_FREE(pbHeaderBuffer); - if(phMpq != NULL) - *phMpq = ha; - return (nError == ERROR_SUCCESS); -} - - -#ifdef FULL -//----------------------------------------------------------------------------- -// bool STORMAPI SFileSetDownloadCallback(HANDLE, SFILE_DOWNLOAD_CALLBACK, void *); -// -// Sets a callback that is called when content is downloaded from the master MPQ -// - -bool STORMAPI 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 STORMAPI SFileFlushArchive(HANDLE hMpq) -{ - TMPQArchive * ha; - int nResultError = ERROR_SUCCESS; - int nError; - - // 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) - { -#ifdef FULL - nError = SSignFileCreate(ha); - if(nError != ERROR_SUCCESS) - nResultError = nError; -#else - assert(0); -#endif - } - - if(ha->dwFlags & MPQ_FLAG_LISTFILE_NEW) - { - nError = SListFileSaveToMpq(ha); - if(nError != ERROR_SUCCESS) - nResultError = nError; - } - - if(ha->dwFlags & MPQ_FLAG_ATTRIBUTES_NEW) - { - nError = SAttrFileSaveToMpq(ha); - if(nError != ERROR_SUCCESS) - nResultError = nError; - } - - // 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 - nError = SaveMPQTables(ha); - if(nError != ERROR_SUCCESS) - nResultError = nError; - - // If the archive has weak signature, we need to finish it - if(ha->dwFileFlags3 != 0) - { -#ifdef FULL - nError = SSignFileFinish(ha); - if(nError != ERROR_SUCCESS) - nResultError = nError; -#else - assert(0); -#endif - } - } - - // We are no longer saving internal MPQ structures - ha->dwFlags &= ~MPQ_FLAG_SAVING_TABLES; - } - - // Return the error - if(nResultError != ERROR_SUCCESS) - SetLastError(nResultError); - return (nResultError == ERROR_SUCCESS); -} -#endif - -//----------------------------------------------------------------------------- -// bool SFileCloseArchive(HANDLE hMpq); -// - -bool STORMAPI SFileCloseArchive(HANDLE hMpq) -{ - TMPQArchive * ha = IsValidMpqHandle(hMpq); - bool bResult = true; - - // 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; - -#ifdef FULL - // Flush all unsaved data to the storage - bResult = SFileFlushArchive(hMpq); -#endif - - // Free all memory used by MPQ archive - FreeArchiveHandle(ha); - return bResult; -} +/*****************************************************************************/ +/* 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]); + + // Test for AVI files (Warcraft III cinematics) - 'RIFF', 'AVI ' or 'LIST' + if(DwordValue0 == 0x46464952 && DwordValue2 == 0x20495641 && DwordValue3 == 0x5453494C) + return MapTypeAviFile; + + // Check for Starcraft II maps + if((szExtension = _tcsrchr(szFileName, _T('.'))) != NULL) + { + // The "NP_Protect" protector places fake Warcraft III header + // into the Starcraft II maps, whilst SC2 maps have no other header but MPQ v4 + if(!_tcsicmp(szExtension, _T(".s2ma")) || !_tcsicmp(szExtension, _T(".SC2Map")) || !_tcsicmp(szExtension, _T(".SC2Mod"))) + { + return MapTypeStarcraft2; + } + } + + // 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 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 int 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 + 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 lcNewLocale) +{ + g_lcFileLocale = lcNewLocale; + return g_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; + int nError = 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(nError == ERROR_SUCCESS) + { + FileStream_GetSize(pStream, &FileSize); + if(FileSize < MPQ_HEADER_SIZE_V1) + nError = ERROR_BAD_FORMAT; + } + + // Allocate the MPQhandle + if(nError == ERROR_SUCCESS) + { + if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL) + nError = ERROR_NOT_ENOUGH_MEMORY; + } + + // Allocate buffer for searching MPQ header + if(nError == ERROR_SUCCESS) + { + pbHeaderBuffer = STORM_ALLOC(BYTE, HEADER_SEARCH_BUFFER_SIZE); + if(pbHeaderBuffer == NULL) + nError = ERROR_NOT_ENOUGH_MEMORY; + } + + // Find the position of MPQ header + if(nError == ERROR_SUCCESS) + { + ULONGLONG ByteOffset = 0; + ULONGLONG EndOfSearch = FileSize; + DWORD dwStrmFlags = 0; + DWORD dwHeaderSize; + DWORD dwHeaderID; + bool bSearchComplete = false; + + memset(ha, 0, sizeof(TMPQArchive)); + 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; + + // 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)) + { + nError = 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) + { + nError = 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 + nError = ConvertMpqHeaderToFormat4(ha, ByteOffset, FileSize, dwFlags, MapType); + if(nError != 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 + nError = 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) + { + nError = ERROR_NOT_SUPPORTED; + bSearchComplete = true; + break; + } + + // Move the pointers + ByteOffset += 0x200; + } + } + + // Did we identify one of the supported headers? + if(nError == 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) + nError = ERROR_BAD_FORMAT; + } + } + + // Fix table positions according to format + if(nError == 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; + + // Remember whether whis is a map for Warcraft III + if(MapType == MapTypeWarcraft3) + ha->dwFlags |= MPQ_FLAG_WAR3_MAP; + + // 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 + nError = 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(nError == ERROR_SUCCESS) + { + nError = 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(nError == ERROR_SUCCESS) + { + nError = BuildFileTable(ha); + } + + // Load the internal listfile and include it to the file table + if(nError == 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(nError == 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(nError == 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(nError != ERROR_SUCCESS) + { + FileStream_Close(pStream); + FreeArchiveHandle(ha); + SetLastError(nError); + ha = NULL; + } + + // Free the header buffer + if(pbHeaderBuffer != NULL) + STORM_FREE(pbHeaderBuffer); + if(phMpq != NULL) + *phMpq = ha; + return (nError == 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; + int nResultError = ERROR_SUCCESS; + int nError; + + // 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) + { + nError = SSignFileCreate(ha); + if(nError != ERROR_SUCCESS) + nResultError = nError; + } + + if(ha->dwFlags & (MPQ_FLAG_LISTFILE_NEW | MPQ_FLAG_LISTFILE_FORCE)) + { + nError = SListFileSaveToMpq(ha); + if(nError != ERROR_SUCCESS) + nResultError = nError; + } + + if(ha->dwFlags & MPQ_FLAG_ATTRIBUTES_NEW) + { + nError = SAttrFileSaveToMpq(ha); + if(nError != ERROR_SUCCESS) + nResultError = nError; + } + + // 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 + nError = SaveMPQTables(ha); + if(nError != ERROR_SUCCESS) + nResultError = nError; + + // If the archive has weak signature, we need to finish it + if(ha->dwFileFlags3 != 0) + { + nError = SSignFileFinish(ha); + if(nError != ERROR_SUCCESS) + nResultError = nError; + } + } + + // We are no longer saving internal MPQ structures + ha->dwFlags &= ~MPQ_FLAG_SAVING_TABLES; + } + + // Return the error + if(nResultError != ERROR_SUCCESS) + SetLastError(nResultError); + return (nResultError == 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; +} diff --git a/3rdParty/StormLib/src/SFileOpenFileEx.cpp b/3rdParty/StormLib/src/SFileOpenFileEx.cpp index 71ca90443..1e02a2202 100644 --- a/3rdParty/StormLib/src/SFileOpenFileEx.cpp +++ b/3rdParty/StormLib/src/SFileOpenFileEx.cpp @@ -124,37 +124,38 @@ bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, HANDLE * PtrFile) } // If we couldn't find the base file in any of the patches, it doesn't exist - if((ha = haBase) == NULL) + if((ha = haBase) != NULL) { - SetLastError(ERROR_FILE_NOT_FOUND); - return false; - } - - // 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) + // Now open the base file + if(SFileOpenFileEx((HANDLE)ha, GetPatchFileName(ha, szFileName, szNameBuffer), SFILE_OPEN_BASE_FILE, (HANDLE *)&hfBase)) { - // Prepare the file name with a correct prefix - if(SFileOpenFileEx((HANDLE)ha, GetPatchFileName(ha, szFileName, szNameBuffer), SFILE_OPEN_BASE_FILE, &hPatchFile)) + // 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) { - // Remember the new version - hfPatch = (TMPQFile *)hPatchFile; + // 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); + // 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; + // 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) @@ -167,12 +168,12 @@ bool OpenPatchedFile(HANDLE hMpq, const char * szFileName, HANDLE * PtrFile) /*****************************************************************************/ //----------------------------------------------------------------------------- -// SFileEnumLocales enums all locale versions within MPQ. +// 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. -int STORMAPI SFileEnumLocales( +int WINAPI SFileEnumLocales( HANDLE hMpq, const char * szFileName, LCID * PtrLocales, @@ -197,7 +198,7 @@ int STORMAPI SFileEnumLocales( return ERROR_INVALID_PARAMETER; if(IsPseudoFileName(szFileName, &dwFileIndex)) return ERROR_INVALID_PARAMETER; - + // Keep compiler happy dwMaxLocales = PtrMaxLocales[0]; dwSearchScope = dwSearchScope; @@ -228,7 +229,7 @@ int STORMAPI SFileEnumLocales( // dwSearchScope - Where to search // PtrFile - Pointer to store opened file handle -bool STORMAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * PtrFile) +bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * PtrFile) { TMPQArchive * ha = IsValidMpqHandle(hMpq); TFileEntry * pFileEntry = NULL; @@ -258,11 +259,11 @@ bool STORMAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSear 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, lcFileLocale, &dwHashIndex); + pFileEntry = GetFileEntryLocale2(ha, szFileName, g_lcFileLocale, &dwHashIndex); } // If this MPQ is a patched archive, open the file as patched @@ -283,7 +284,7 @@ bool STORMAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSear case SFILE_OPEN_LOCAL_FILE: // Open a local file - return OpenLocalFile(szFileName, PtrFile); + return OpenLocalFile(szFileName, PtrFile); default: @@ -296,24 +297,45 @@ bool STORMAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSear // Check whether the file really exists in the MPQ if(nError == ERROR_SUCCESS) { - if(pFileEntry == NULL || (pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0) + // 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 - if((bOpenByIndex = IsPseudoFileName(szFileName, &dwFileIndex)) == true) + // Check the pseudo-file name ("File00000001.ext") + if ((bOpenByIndex = IsPseudoFileName(szFileName, &dwFileIndex)) == true) { // Get the file entry for the file - if(dwFileIndex < ha->dwFileTableSize) + if (dwFileIndex < ha->dwFileTableSize) { pFileEntry = ha->pFileTable + dwFileIndex; } } - nError = ERROR_FILE_NOT_FOUND; + // Still not found? + if (pFileEntry == NULL) + { + nError = ERROR_FILE_NOT_FOUND; + } } - // Ignore unknown loading flags (example: MPQ_2016_v1_WME4_4.w3x) -// if(pFileEntry != NULL && pFileEntry->dwFlags & ~MPQ_FILE_VALID_FLAGS) -// nError = ERROR_NOT_SUPPORTED; + // 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)) + { + nError = ERROR_FILE_CORRUPT; + pFileEntry = NULL; + } + + // Ignore unknown loading flags (example: MPQ_2016_v1_WME4_4.w3x) +// if(pFileEntry->dwFlags & ~MPQ_FILE_VALID_FLAGS) +// { +// nError = ERROR_NOT_SUPPORTED; +// pFileEntry = NULL; +// } + } } // Did the caller just wanted to know if the file exists? @@ -358,7 +380,7 @@ bool STORMAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSear // Give the file entry if(PtrFile != NULL) - *PtrFile = hf; + PtrFile[0] = hf; // Return error code if(nError != ERROR_SUCCESS) @@ -372,18 +394,18 @@ bool STORMAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSear // hMpq - Handle of opened MPQ archive // szFileName - Name of file to look for -bool STORMAPI SFileHasFile(HANDLE hMpq, const char * szFileName) +bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName) { return SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_CHECK_EXISTS, NULL); } //----------------------------------------------------------------------------- -// bool STORMAPI SFileCloseFile(HANDLE hFile); +// bool WINAPI SFileCloseFile(HANDLE hFile); -bool STORMAPI SFileCloseFile(HANDLE hFile) +bool WINAPI SFileCloseFile(HANDLE hFile) { TMPQFile * hf = (TMPQFile *)hFile; - + if(!IsValidFileHandle(hFile)) { SetLastError(ERROR_INVALID_HANDLE); diff --git a/3rdParty/StormLib/src/SFileReadFile.cpp b/3rdParty/StormLib/src/SFileReadFile.cpp index f8eb9d50e..192d212f6 100644 --- a/3rdParty/StormLib/src/SFileReadFile.cpp +++ b/3rdParty/StormLib/src/SFileReadFile.cpp @@ -1,913 +1,897 @@ -/*****************************************************************************/ -/* SFileReadFile.cpp Copyright (c) Ladislav Zezula 2003 */ -/*---------------------------------------------------------------------------*/ -/* Description : */ -/*---------------------------------------------------------------------------*/ -/* Date Ver Who Comment */ -/* -------- ---- --- ------- */ -/* xx.xx.99 1.00 Lad The first version of SFileReadFile.cpp */ -/* 24.03.99 1.00 Lad Added the SFileGetFileInfo function */ -/*****************************************************************************/ - -#define __STORMLIB_SELF__ -#include "StormLib.h" -#include "StormCommon.h" - -//----------------------------------------------------------------------------- -// Local functions - -// hf - MPQ File handle. -// pbBuffer - Pointer to target buffer to store sectors. -// dwByteOffset - Position of sector in the file (relative to file begin) -// dwBytesToRead - Number of bytes to read. Must be multiplier of sector size. -// pdwBytesRead - Stored number of bytes loaded -static int ReadMpqSectors(TMPQFile * hf, LPBYTE pbBuffer, DWORD dwByteOffset, DWORD dwBytesToRead, LPDWORD pdwBytesRead) -{ - ULONGLONG RawFilePos; - TMPQArchive * ha = hf->ha; - TFileEntry * pFileEntry = hf->pFileEntry; - LPBYTE pbRawSector = NULL; - LPBYTE pbOutSector = pbBuffer; - LPBYTE pbInSector = pbBuffer; - DWORD dwRawBytesToRead; - DWORD dwRawSectorOffset = dwByteOffset; - DWORD dwSectorsToRead = dwBytesToRead / ha->dwSectorSize; - DWORD dwSectorIndex = dwByteOffset / ha->dwSectorSize; - DWORD dwSectorsDone = 0; - DWORD dwBytesRead = 0; - int nError = ERROR_SUCCESS; - - // Note that dwByteOffset must be aligned to size of one sector - // Note that dwBytesToRead must be a multiplier of one sector size - // This is local function, so we won't check if that's true. - // Note that files stored in single units are processed by a separate function - - // If there is not enough bytes remaining, cut dwBytesToRead - if((dwByteOffset + dwBytesToRead) > hf->dwDataSize) - dwBytesToRead = hf->dwDataSize - dwByteOffset; - dwRawBytesToRead = dwBytesToRead; - - // Perform all necessary work to do with compressed files - if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) - { - // If the sector positions are not loaded yet, do it - if(hf->SectorOffsets == NULL) - { - nError = AllocateSectorOffsets(hf, true); - if(nError != ERROR_SUCCESS || hf->SectorOffsets == NULL) - return nError; - } - - // If the sector checksums are not loaded yet, load them now. - if(hf->SectorChksums == NULL && (pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC) && hf->bLoadedSectorCRCs == false) - { - // - // Sector CRCs is plain crap feature. It is almost never present, - // often it's empty, or the end offset of sector CRCs is zero. - // We only try to load sector CRCs once, and regardless if it fails - // or not, we won't try that again for the given file. - // - - AllocateSectorChecksums(hf, true); - hf->bLoadedSectorCRCs = true; - } - - // TODO: If the raw data MD5s are not loaded yet, load them now - // Only do it if the MPQ is of format 4.0 -// if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_4 && ha->pHeader->dwRawChunkSize != 0) -// { -// nError = AllocateRawMD5s(hf, true); -// if(nError != ERROR_SUCCESS) -// return nError; -// } - - // Assign the temporary buffer as target for read operation - dwRawSectorOffset = hf->SectorOffsets[dwSectorIndex]; - dwRawBytesToRead = hf->SectorOffsets[dwSectorIndex + dwSectorsToRead] - dwRawSectorOffset; - - // If the file is compressed, also allocate secondary buffer - pbInSector = pbRawSector = STORM_ALLOC(BYTE, dwRawBytesToRead); - if(pbRawSector == NULL) - return ERROR_NOT_ENOUGH_MEMORY; - } - - // Calculate raw file offset where the sector(s) are stored. - RawFilePos = CalculateRawSectorOffset(hf, dwRawSectorOffset); - - // Set file pointer and read all required sectors - if(FileStream_Read(ha->pStream, &RawFilePos, pbInSector, dwRawBytesToRead)) - { - // Now we have to decrypt and decompress all file sectors that have been loaded - for(DWORD i = 0; i < dwSectorsToRead; i++) - { - DWORD dwRawBytesInThisSector = ha->dwSectorSize; - DWORD dwBytesInThisSector = ha->dwSectorSize; - DWORD dwIndex = dwSectorIndex + i; - - // If there is not enough bytes in the last sector, - // cut the number of bytes in this sector - if(dwRawBytesInThisSector > dwBytesToRead) - dwRawBytesInThisSector = dwBytesToRead; - if(dwBytesInThisSector > dwBytesToRead) - dwBytesInThisSector = dwBytesToRead; - - // If the file is compressed, we have to adjust the raw sector size - if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) - dwRawBytesInThisSector = hf->SectorOffsets[dwIndex + 1] - hf->SectorOffsets[dwIndex]; - - // If the file is encrypted, we have to decrypt the sector - if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) - { - BSWAP_ARRAY32_UNSIGNED(pbInSector, dwRawBytesInThisSector); - - // If we don't know the key, try to detect it by file content - if(hf->dwFileKey == 0) - { - hf->dwFileKey = DetectFileKeyByContent(pbInSector, dwBytesInThisSector, hf->dwDataSize); - if(hf->dwFileKey == 0) - { - nError = ERROR_UNKNOWN_FILE_KEY; - break; - } - } - - DecryptMpqBlock(pbInSector, dwRawBytesInThisSector, hf->dwFileKey + dwIndex); - BSWAP_ARRAY32_UNSIGNED(pbInSector, dwRawBytesInThisSector); - } - -#ifdef FULL - // If the file has sector CRC check turned on, perform it - if(hf->bCheckSectorCRCs && hf->SectorChksums != NULL) - { - DWORD dwAdlerExpected = hf->SectorChksums[dwIndex]; - DWORD dwAdlerValue = 0; - - // We can only check sector CRC when it's not zero - // Neither can we check it if it's 0xFFFFFFFF. - if(dwAdlerExpected != 0 && dwAdlerExpected != 0xFFFFFFFF) - { - dwAdlerValue = adler32(0, pbInSector, dwRawBytesInThisSector); - if(dwAdlerValue != dwAdlerExpected) - { - nError = ERROR_CHECKSUM_ERROR; - break; - } - } - } -#endif - - // If the sector is really compressed, decompress it. - // WARNING : Some sectors may not be compressed, it can be determined only - // by comparing uncompressed and compressed size !!! - if(dwRawBytesInThisSector < dwBytesInThisSector) - { - int cbOutSector = dwBytesInThisSector; - int cbInSector = dwRawBytesInThisSector; - int nResult = 0; - - // Is the file compressed by Blizzard's multiple compression ? - if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS) - { - // Remember the last used compression - hf->dwCompression0 = pbInSector[0]; - - // Decompress the data - if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2) - nResult = SCompDecompress2(pbOutSector, &cbOutSector, pbInSector, cbInSector); - else - nResult = SCompDecompress(pbOutSector, &cbOutSector, pbInSector, cbInSector); - } - - // Is the file compressed by PKWARE Data Compression Library ? - else if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE) - { - nResult = SCompExplode(pbOutSector, &cbOutSector, pbInSector, cbInSector); - } - - // Did the decompression fail ? - if(nResult == 0) - { - nError = ERROR_FILE_CORRUPT; - break; - } - } - else - { - if(pbOutSector != pbInSector) - memcpy(pbOutSector, pbInSector, dwBytesInThisSector); - } - - // Move pointers - dwBytesToRead -= dwBytesInThisSector; - dwByteOffset += dwBytesInThisSector; - dwBytesRead += dwBytesInThisSector; - pbOutSector += dwBytesInThisSector; - pbInSector += dwRawBytesInThisSector; - dwSectorsDone++; - } - } - else - { - nError = GetLastError(); - } - - // Free all used buffers - if(pbRawSector != NULL) - STORM_FREE(pbRawSector); - - // Give the caller thenumber of bytes read - *pdwBytesRead = dwBytesRead; - return nError; -} - -static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead) -{ - ULONGLONG RawFilePos = hf->RawFilePos; - TMPQArchive * ha = hf->ha; - TFileEntry * pFileEntry = hf->pFileEntry; - LPBYTE pbCompressed = NULL; - LPBYTE pbRawData; - int nError = ERROR_SUCCESS; - - // If the file buffer is not allocated yet, do it. - if(hf->pbFileSector == NULL) - { - nError = AllocateSectorBuffer(hf); - if(nError != ERROR_SUCCESS || hf->pbFileSector == NULL) - return nError; - } - - // If the file is a patch file, adjust raw data offset - if(hf->pPatchInfo != NULL) - RawFilePos += hf->pPatchInfo->dwLength; - pbRawData = hf->pbFileSector; - - // If the file sector is not loaded yet, do it - if(hf->dwSectorOffs != 0) - { - // Is the file compressed? - if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) - { - // Allocate space for compressed data - pbCompressed = STORM_ALLOC(BYTE, pFileEntry->dwCmpSize); - if(pbCompressed == NULL) - return ERROR_NOT_ENOUGH_MEMORY; - pbRawData = pbCompressed; - } - - // Load the raw (compressed, encrypted) data - if(!FileStream_Read(ha->pStream, &RawFilePos, pbRawData, pFileEntry->dwCmpSize)) - { - STORM_FREE(pbCompressed); - return GetLastError(); - } - - // If the file is encrypted, we have to decrypt the data first - if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) - { - BSWAP_ARRAY32_UNSIGNED(pbRawData, pFileEntry->dwCmpSize); - DecryptMpqBlock(pbRawData, pFileEntry->dwCmpSize, hf->dwFileKey); - BSWAP_ARRAY32_UNSIGNED(pbRawData, pFileEntry->dwCmpSize); - } - - // If the file is compressed, we have to decompress it now - if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) - { - int cbOutBuffer = (int)hf->dwDataSize; - int cbInBuffer = (int)pFileEntry->dwCmpSize; - int nResult = 0; - - // - // If the file is an incremental patch, the size of compressed data - // is determined as pFileEntry->dwCmpSize - sizeof(TPatchInfo) - // - // In "wow-update-12694.MPQ" from Wow-Cataclysm BETA: - // - // File CmprSize DcmpSize DataSize Compressed? - // -------------------------------------- ---------- -------- -------- --------------- - // esES\DBFilesClient\LightSkyBox.dbc 0xBE->0xA2 0xBC 0xBC Yes - // deDE\DBFilesClient\MountCapability.dbc 0x93->0x77 0x77 0x77 No - // - - if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) - cbInBuffer = cbInBuffer - sizeof(TPatchInfo); - - // Is the file compressed by Blizzard's multiple compression ? - if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS) - { - // Remember the last used compression - hf->dwCompression0 = pbRawData[0]; - - // Decompress the file - if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2) - nResult = SCompDecompress2(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer); - else - nResult = SCompDecompress(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer); - } - - // Is the file compressed by PKWARE Data Compression Library ? - // Note: Single unit files compressed with IMPLODE are not supported by Blizzard - else if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE) - nResult = SCompExplode(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer); - - nError = (nResult != 0) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT; - } - else - { - if(hf->pbFileSector != NULL && pbRawData != hf->pbFileSector) - memcpy(hf->pbFileSector, pbRawData, hf->dwDataSize); - } - - // Free the decompression buffer. - if(pbCompressed != NULL) - STORM_FREE(pbCompressed); - - // The file sector is now properly loaded - hf->dwSectorOffs = 0; - } - - // At this moment, we have the file loaded into the file buffer. - // Copy as much as the caller wants - if(nError == ERROR_SUCCESS && hf->dwSectorOffs == 0) - { - // File position is greater or equal to file size ? - if(dwFilePos >= hf->dwDataSize) - { - *pdwBytesRead = 0; - return ERROR_SUCCESS; - } - - // If not enough bytes remaining in the file, cut them - if((hf->dwDataSize - dwFilePos) < dwToRead) - dwToRead = (hf->dwDataSize - dwFilePos); - - // Copy the bytes - memcpy(pvBuffer, hf->pbFileSector + dwFilePos, dwToRead); - - // Give the number of bytes read - *pdwBytesRead = dwToRead; - return ERROR_SUCCESS; - } - - // An error, sorry - return ERROR_CAN_NOT_COMPLETE; -} - -static int ReadMpkFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead) -{ - ULONGLONG RawFilePos = hf->RawFilePos + 0x0C; // For some reason, MPK files start at position (hf->RawFilePos + 0x0C) - TMPQArchive * ha = hf->ha; - TFileEntry * pFileEntry = hf->pFileEntry; - LPBYTE pbCompressed = NULL; - LPBYTE pbRawData = hf->pbFileSector; - int nError = ERROR_SUCCESS; - - // We do not support patch files in MPK archives - assert(hf->pPatchInfo == NULL); - - // If the file buffer is not allocated yet, do it. - if(hf->pbFileSector == NULL) - { - nError = AllocateSectorBuffer(hf); - if(nError != ERROR_SUCCESS || hf->pbFileSector == NULL) - return nError; - pbRawData = hf->pbFileSector; - } - - // If the file sector is not loaded yet, do it - if(hf->dwSectorOffs != 0) - { - // Is the file compressed? - if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) - { - // Allocate space for compressed data - pbCompressed = STORM_ALLOC(BYTE, pFileEntry->dwCmpSize); - if(pbCompressed == NULL) - return ERROR_NOT_ENOUGH_MEMORY; - pbRawData = pbCompressed; - } - - // Load the raw (compressed, encrypted) data - if(!FileStream_Read(ha->pStream, &RawFilePos, pbRawData, pFileEntry->dwCmpSize)) - { - STORM_FREE(pbCompressed); - return GetLastError(); - } - - // If the file is encrypted, we have to decrypt the data first - if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) - { - DecryptMpkTable(pbRawData, pFileEntry->dwCmpSize); - } - - // If the file is compressed, we have to decompress it now - if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) - { -#ifdef FULL - int cbOutBuffer = (int)hf->dwDataSize; - - hf->dwCompression0 = pbRawData[0]; - if(!SCompDecompressMpk(hf->pbFileSector, &cbOutBuffer, pbRawData, (int)pFileEntry->dwCmpSize)) - nError = ERROR_FILE_CORRUPT; -#else - assert(0); -#endif - } - else - { - if(pbRawData != hf->pbFileSector) - memcpy(hf->pbFileSector, pbRawData, hf->dwDataSize); - } - - // Free the decompression buffer. - if(pbCompressed != NULL) - STORM_FREE(pbCompressed); - - // The file sector is now properly loaded - hf->dwSectorOffs = 0; - } - - // At this moment, we have the file loaded into the file buffer. - // Copy as much as the caller wants - if(nError == ERROR_SUCCESS && hf->dwSectorOffs == 0) - { - // File position is greater or equal to file size ? - if(dwFilePos >= hf->dwDataSize) - { - *pdwBytesRead = 0; - return ERROR_SUCCESS; - } - - // If not enough bytes remaining in the file, cut them - if((hf->dwDataSize - dwFilePos) < dwToRead) - dwToRead = (hf->dwDataSize - dwFilePos); - - // Copy the bytes - memcpy(pvBuffer, hf->pbFileSector + dwFilePos, dwToRead); - - // Give the number of bytes read - *pdwBytesRead = dwToRead; - return ERROR_SUCCESS; - } - - // An error, sorry - return ERROR_CAN_NOT_COMPLETE; -} - - -static int ReadMpqFileSectorFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwBytesToRead, LPDWORD pdwBytesRead) -{ - TMPQArchive * ha = hf->ha; - LPBYTE pbBuffer = (BYTE *)pvBuffer; - DWORD dwTotalBytesRead = 0; // Total bytes read in all three parts - DWORD dwSectorSizeMask = ha->dwSectorSize - 1; // Mask for block size, usually 0x0FFF - DWORD dwFileSectorPos; // File offset of the loaded sector - DWORD dwBytesRead; // Number of bytes read (temporary variable) - int nError; - - // If the file position is at or beyond end of file, do nothing - if(dwFilePos >= hf->dwDataSize) - { - *pdwBytesRead = 0; - return ERROR_SUCCESS; - } - - // If not enough bytes in the file remaining, cut them - if(dwBytesToRead > (hf->dwDataSize - dwFilePos)) - dwBytesToRead = (hf->dwDataSize - dwFilePos); - - // Compute sector position in the file - dwFileSectorPos = dwFilePos & ~dwSectorSizeMask; // Position in the block - - // If the file sector buffer is not allocated yet, do it now - if(hf->pbFileSector == NULL) - { - nError = AllocateSectorBuffer(hf); - if(nError != ERROR_SUCCESS || hf->pbFileSector == NULL) - return nError; - } - - // Load the first (incomplete) file sector - if(dwFilePos & dwSectorSizeMask) - { - DWORD dwBytesInSector = ha->dwSectorSize; - DWORD dwBufferOffs = dwFilePos & dwSectorSizeMask; - DWORD dwToCopy; - - // Is the file sector already loaded ? - if(hf->dwSectorOffs != dwFileSectorPos) - { - // Load one MPQ sector into archive buffer - nError = ReadMpqSectors(hf, hf->pbFileSector, dwFileSectorPos, ha->dwSectorSize, &dwBytesInSector); - if(nError != ERROR_SUCCESS) - return nError; - - // Remember that the data loaded to the sector have new file offset - hf->dwSectorOffs = dwFileSectorPos; - } - else - { - if((dwFileSectorPos + dwBytesInSector) > hf->dwDataSize) - dwBytesInSector = hf->dwDataSize - dwFileSectorPos; - } - - // Copy the data from the offset in the loaded sector to the end of the sector - dwToCopy = dwBytesInSector - dwBufferOffs; - if(dwToCopy > dwBytesToRead) - dwToCopy = dwBytesToRead; - - // Copy data from sector buffer into target buffer - memcpy(pbBuffer, hf->pbFileSector + dwBufferOffs, dwToCopy); - - // Update pointers and byte counts - dwTotalBytesRead += dwToCopy; - dwFileSectorPos += dwBytesInSector; - pbBuffer += dwToCopy; - dwBytesToRead -= dwToCopy; - } - - // Load the whole ("middle") sectors only if there is at least one full sector to be read - if(dwBytesToRead >= ha->dwSectorSize) - { - DWORD dwBlockBytes = dwBytesToRead & ~dwSectorSizeMask; - - // Load all sectors to the output buffer - nError = ReadMpqSectors(hf, pbBuffer, dwFileSectorPos, dwBlockBytes, &dwBytesRead); - if(nError != ERROR_SUCCESS) - return nError; - - // Update pointers - dwTotalBytesRead += dwBytesRead; - dwFileSectorPos += dwBytesRead; - pbBuffer += dwBytesRead; - dwBytesToRead -= dwBytesRead; - } - - // Read the terminating sector - if(dwBytesToRead > 0) - { - DWORD dwToCopy = ha->dwSectorSize; - - // Is the file sector already loaded ? - if(hf->dwSectorOffs != dwFileSectorPos) - { - // Load one MPQ sector into archive buffer - nError = ReadMpqSectors(hf, hf->pbFileSector, dwFileSectorPos, ha->dwSectorSize, &dwBytesRead); - if(nError != ERROR_SUCCESS) - return nError; - - // Remember that the data loaded to the sector have new file offset - hf->dwSectorOffs = dwFileSectorPos; - } - - // Check number of bytes read - if(dwToCopy > dwBytesToRead) - dwToCopy = dwBytesToRead; - - // Copy the data from the cached last sector to the caller's buffer - memcpy(pbBuffer, hf->pbFileSector, dwToCopy); - - // Update pointers - dwTotalBytesRead += dwToCopy; - } - - // Store total number of bytes read to the caller - *pdwBytesRead = dwTotalBytesRead; - return ERROR_SUCCESS; -} - -#ifdef FULL -static int ReadMpqFilePatchFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead) -{ - TMPQPatcher Patcher; - DWORD dwBytesToRead = dwToRead; - DWORD dwBytesRead = 0; - int nError = ERROR_SUCCESS; - - // Make sure that the patch file is loaded completely - if(nError == ERROR_SUCCESS && hf->pbFileData == NULL) - { - // Initialize patching process and allocate data - nError = Patch_InitPatcher(&Patcher, hf); - if(nError != ERROR_SUCCESS) - return nError; - - // Set the current data size - Patcher.cbFileData = hf->pFileEntry->dwFileSize; - - // Initialize the patcher object with initial file data - if(hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) - nError = ReadMpqFileSingleUnit(hf, Patcher.pbFileData1, 0, Patcher.cbFileData, &dwBytesRead); - else - nError = ReadMpqFileSectorFile(hf, Patcher.pbFileData1, 0, Patcher.cbFileData, &dwBytesRead); - - // Perform the patching process - if(nError == ERROR_SUCCESS) - nError = Patch_Process(&Patcher, hf); - - // Finalize the patcher structure - Patch_Finalize(&Patcher); - dwBytesRead = 0; - } - - // If there is something to read, do it - if(nError == ERROR_SUCCESS) - { - if(dwFilePos < hf->cbFileData) - { - // Make sure we don't copy more than file size - if((dwFilePos + dwToRead) > hf->cbFileData) - dwToRead = hf->cbFileData - dwFilePos; - - // Copy the appropriate amount of the file data to the caller's buffer - memcpy(pvBuffer, hf->pbFileData + dwFilePos, dwToRead); - dwBytesRead = dwToRead; - } - - // Set the proper error code - nError = (dwBytesRead == dwBytesToRead) ? ERROR_SUCCESS : ERROR_HANDLE_EOF; - } - - // Give the result to the caller - if(pdwBytesRead != NULL) - *pdwBytesRead = dwBytesRead; - return nError; -} -#endif - -static int ReadMpqFileLocalFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead) -{ - ULONGLONG FilePosition1 = dwFilePos; - ULONGLONG FilePosition2; - DWORD dwBytesRead = 0; - int nError = ERROR_SUCCESS; - - assert(hf->pStream != NULL); - - // Because stream I/O functions are designed to read - // "all or nothing", we compare file position before and after, - // and if they differ, we assume that number of bytes read - // is the difference between them - - if(!FileStream_Read(hf->pStream, &FilePosition1, pvBuffer, dwToRead)) - { - // If not all bytes have been read, then return the number of bytes read - if((nError = GetLastError()) == ERROR_HANDLE_EOF) - { - FileStream_GetPos(hf->pStream, &FilePosition2); - dwBytesRead = (DWORD)(FilePosition2 - FilePosition1); - } - } - else - { - dwBytesRead = dwToRead; - } - - *pdwBytesRead = dwBytesRead; - return nError; -} - -//----------------------------------------------------------------------------- -// SFileReadFile - -bool STORMAPI SFileReadFile(HANDLE hFile, void * pvBuffer, DWORD dwToRead, LPDWORD pdwRead, LPOVERLAPPED lpOverlapped) -{ - TMPQFile * hf = (TMPQFile *)hFile; - DWORD dwBytesRead = 0; // Number of bytes read - int nError = ERROR_SUCCESS; - - // Always zero the result - if(pdwRead != NULL) - *pdwRead = 0; - lpOverlapped = lpOverlapped; - - // Check valid parameters - if(!IsValidFileHandle(hFile)) - { - SetLastError(ERROR_INVALID_HANDLE); - return false; - } - - if(pvBuffer == NULL) - { - SetLastError(ERROR_INVALID_PARAMETER); - return false; - } - - // If we didn't load the patch info yet, do it now - if(hf->pFileEntry != NULL && (hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) && hf->pPatchInfo == NULL) - { - nError = AllocatePatchInfo(hf, true); - if(nError != ERROR_SUCCESS || hf->pPatchInfo == NULL) - { - SetLastError(nError); - return false; - } - } - - // Clear the last used compression - hf->dwCompression0 = 0; - - // If the file is local file, read the data directly from the stream - if(hf->pStream != NULL) - { - ULONGLONG pos; - if (!FileStream_GetPos(hf->pStream, &pos)) { - SetLastError(ERROR_CAN_NOT_COMPLETE); - return false; - } - nError = ReadMpqFileLocalFile(hf, pvBuffer, pos, dwToRead, &dwBytesRead); - } -#ifdef FULL - // If the file is a patch file, we have to read it special way - else if(hf->hfPatch != NULL && (hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0) - { - nError = ReadMpqFilePatchFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); - } -#endif - // If the archive is a MPK archive, we need special way to read the file - else if(hf->ha->dwSubType == MPQ_SUBTYPE_MPK) - { - nError = ReadMpkFileSingleUnit(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); - } - - // If the file is single unit file, redirect it to read file - else if(hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) - { - nError = ReadMpqFileSingleUnit(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); - } - - // Otherwise read it as sector based MPQ file - else - { - nError = ReadMpqFileSectorFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); - } - - // Increment the file position - if(hf->pStream == NULL) - hf->dwFilePos += dwBytesRead; - - // Give the caller the number of bytes read - if(pdwRead != NULL) - *pdwRead = dwBytesRead; - - // If the read operation succeeded, but not full number of bytes was read, - // set the last error to ERROR_HANDLE_EOF - if(nError == ERROR_SUCCESS && (dwBytesRead < dwToRead)) - nError = ERROR_HANDLE_EOF; - - // If something failed, set the last error value - if(nError != ERROR_SUCCESS) - SetLastError(nError); - return (nError == ERROR_SUCCESS); -} - -//----------------------------------------------------------------------------- -// SFileGetFileSize - -DWORD STORMAPI SFileGetFileSize(HANDLE hFile, LPDWORD pdwFileSizeHigh) -{ - ULONGLONG FileSize; - TMPQFile * hf = (TMPQFile *)hFile; - - // Validate the file handle before we go on - if(IsValidFileHandle(hFile)) - { - // Make sure that the variable is initialized - FileSize = 0; - - // If the file is patched file, we have to get the size of the last version - if(hf->hfPatch != NULL) - { - // Walk through the entire patch chain, take the last version - while(hf != NULL) - { - // Get the size of the currently pointed version - FileSize = hf->pFileEntry->dwFileSize; - - // Move to the next patch file in the hierarchy - hf = hf->hfPatch; - } - } - else - { - // Is it a local file ? - if(hf->pStream != NULL) - { - FileStream_GetSize(hf->pStream, &FileSize); - } - else - { - FileSize = hf->dwDataSize; - } - } - - // If opened from archive, return file size - if(pdwFileSizeHigh != NULL) - *pdwFileSizeHigh = (DWORD)(FileSize >> 32); - return (DWORD)FileSize; - } - - SetLastError(ERROR_INVALID_HANDLE); - return SFILE_INVALID_SIZE; -} - -DWORD STORMAPI SFileSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod) -{ - TMPQFile * hf = (TMPQFile *)hFile; - ULONGLONG OldPosition; - ULONGLONG NewPosition; - ULONGLONG FileSize; - ULONGLONG DeltaPos; - - // If the hFile is not a valid file handle, return an error. - if(!IsValidFileHandle(hFile)) - { - SetLastError(ERROR_INVALID_HANDLE); - return SFILE_INVALID_POS; - } - - // Retrieve the file size for handling the limits - if(hf->pStream != NULL) - { - FileStream_GetSize(hf->pStream, &FileSize); - } - else - { - FileSize = SFileGetFileSize(hFile, NULL); - } - - // Handle the NULL and non-NULL values of plFilePosHigh - // Non-NULL: The DeltaPos is combined from lFilePos and *lpFilePosHigh - // NULL: The DeltaPos is sign-extended value of lFilePos - DeltaPos = (plFilePosHigh != NULL) ? MAKE_OFFSET64(plFilePosHigh[0], lFilePos) : (ULONGLONG)(LONGLONG)lFilePos; - - // Get the relative point where to move from - switch(dwMoveMethod) - { - case FILE_BEGIN: - - // Move relative to the file begin. - OldPosition = 0; - break; - - case FILE_CURRENT: - - // Retrieve the current file position - if(hf->pStream != NULL) - { - FileStream_GetPos(hf->pStream, &OldPosition); - } - else - { - OldPosition = hf->dwFilePos; - } - break; - - case FILE_END: - - // Move relative to the end of the file - OldPosition = FileSize; - break; - - default: - SetLastError(ERROR_INVALID_PARAMETER); - return SFILE_INVALID_POS; - } - - // Calculate the new position - NewPosition = OldPosition + DeltaPos; - - // If moving backward, don't allow the new position go negative - if((LONGLONG)DeltaPos < 0) - { - if(NewPosition > FileSize) // Position is negative - { - SetLastError(ERROR_NEGATIVE_SEEK); - return SFILE_INVALID_POS; - } - } - - // If moving forward, don't allow the new position go past the end of the file - else - { - if(NewPosition > FileSize) - NewPosition = FileSize; - } - - // Now apply the file pointer to the file - if(hf->pStream != NULL) - { - if(!FileStream_Read(hf->pStream, &NewPosition, NULL, 0)) - return SFILE_INVALID_POS; - } - else - { - hf->dwFilePos = (DWORD)NewPosition; - } - - // Return the new file position - if(plFilePosHigh != NULL) - *plFilePosHigh = (LONG)(NewPosition >> 32); - return (DWORD)NewPosition; -} +/*****************************************************************************/ +/* SFileReadFile.cpp Copyright (c) Ladislav Zezula 2003 */ +/*---------------------------------------------------------------------------*/ +/* Description : */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* xx.xx.99 1.00 Lad The first version of SFileReadFile.cpp */ +/* 24.03.99 1.00 Lad Added the SFileGetFileInfo function */ +/*****************************************************************************/ + +#define __STORMLIB_SELF__ +#include "StormLib.h" +#include "StormCommon.h" + +//----------------------------------------------------------------------------- +// Local functions + +// hf - MPQ File handle. +// pbBuffer - Pointer to target buffer to store sectors. +// dwByteOffset - Position of sector in the file (relative to file begin) +// dwBytesToRead - Number of bytes to read. Must be multiplier of sector size. +// pdwBytesRead - Stored number of bytes loaded +static int ReadMpqSectors(TMPQFile * hf, LPBYTE pbBuffer, DWORD dwByteOffset, DWORD dwBytesToRead, LPDWORD pdwBytesRead) +{ + ULONGLONG RawFilePos; + TMPQArchive * ha = hf->ha; + TFileEntry * pFileEntry = hf->pFileEntry; + LPBYTE pbRawSector = NULL; + LPBYTE pbOutSector = pbBuffer; + LPBYTE pbInSector = pbBuffer; + DWORD dwRawBytesToRead; + DWORD dwRawSectorOffset = dwByteOffset; + DWORD dwSectorsToRead = dwBytesToRead / ha->dwSectorSize; + DWORD dwSectorIndex = dwByteOffset / ha->dwSectorSize; + DWORD dwSectorsDone = 0; + DWORD dwBytesRead = 0; + int nError = ERROR_SUCCESS; + + // Note that dwByteOffset must be aligned to size of one sector + // Note that dwBytesToRead must be a multiplier of one sector size + // This is local function, so we won't check if that's true. + // Note that files stored in single units are processed by a separate function + + // If there is not enough bytes remaining, cut dwBytesToRead + if((dwByteOffset + dwBytesToRead) > hf->dwDataSize) + dwBytesToRead = hf->dwDataSize - dwByteOffset; + dwRawBytesToRead = dwBytesToRead; + + // Perform all necessary work to do with compressed files + if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) + { + // If the sector positions are not loaded yet, do it + if(hf->SectorOffsets == NULL) + { + nError = AllocateSectorOffsets(hf, true); + if(nError != ERROR_SUCCESS || hf->SectorOffsets == NULL) + return nError; + } + + // If the sector checksums are not loaded yet, load them now. + if(hf->SectorChksums == NULL && (pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC) && hf->bLoadedSectorCRCs == false) + { + // + // Sector CRCs is plain crap feature. It is almost never present, + // often it's empty, or the end offset of sector CRCs is zero. + // We only try to load sector CRCs once, and regardless if it fails + // or not, we won't try that again for the given file. + // + + AllocateSectorChecksums(hf, true); + hf->bLoadedSectorCRCs = true; + } + + // TODO: If the raw data MD5s are not loaded yet, load them now + // Only do it if the MPQ is of format 4.0 +// if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_4 && ha->pHeader->dwRawChunkSize != 0) +// { +// nError = AllocateRawMD5s(hf, true); +// if(nError != ERROR_SUCCESS) +// return nError; +// } + + // Assign the temporary buffer as target for read operation + dwRawSectorOffset = hf->SectorOffsets[dwSectorIndex]; + dwRawBytesToRead = hf->SectorOffsets[dwSectorIndex + dwSectorsToRead] - dwRawSectorOffset; + + // If the file is compressed, also allocate secondary buffer + pbInSector = pbRawSector = STORM_ALLOC(BYTE, dwRawBytesToRead); + if(pbRawSector == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + } + + // Calculate raw file offset where the sector(s) are stored. + RawFilePos = CalculateRawSectorOffset(hf, dwRawSectorOffset); + + // Set file pointer and read all required sectors + if(FileStream_Read(ha->pStream, &RawFilePos, pbInSector, dwRawBytesToRead)) + { + // Now we have to decrypt and decompress all file sectors that have been loaded + for(DWORD i = 0; i < dwSectorsToRead; i++) + { + DWORD dwRawBytesInThisSector = ha->dwSectorSize; + DWORD dwBytesInThisSector = ha->dwSectorSize; + DWORD dwIndex = dwSectorIndex + i; + + // If there is not enough bytes in the last sector, + // cut the number of bytes in this sector + if(dwRawBytesInThisSector > dwBytesToRead) + dwRawBytesInThisSector = dwBytesToRead; + if(dwBytesInThisSector > dwBytesToRead) + dwBytesInThisSector = dwBytesToRead; + + // If the file is compressed, we have to adjust the raw sector size + if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) + dwRawBytesInThisSector = hf->SectorOffsets[dwIndex + 1] - hf->SectorOffsets[dwIndex]; + + // If the file is encrypted, we have to decrypt the sector + if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) + { + BSWAP_ARRAY32_UNSIGNED(pbInSector, dwRawBytesInThisSector); + + // If we don't know the key, try to detect it by file content + if(hf->dwFileKey == 0) + { + hf->dwFileKey = DetectFileKeyByContent(pbInSector, dwBytesInThisSector, hf->dwDataSize); + if(hf->dwFileKey == 0) + { + nError = ERROR_UNKNOWN_FILE_KEY; + break; + } + } + + DecryptMpqBlock(pbInSector, dwRawBytesInThisSector, hf->dwFileKey + dwIndex); + BSWAP_ARRAY32_UNSIGNED(pbInSector, dwRawBytesInThisSector); + } + + // If the file has sector CRC check turned on, perform it + if(hf->bCheckSectorCRCs && hf->SectorChksums != NULL) + { + DWORD dwAdlerExpected = hf->SectorChksums[dwIndex]; + DWORD dwAdlerValue = 0; + + // We can only check sector CRC when it's not zero + // Neither can we check it if it's 0xFFFFFFFF. + if(dwAdlerExpected != 0 && dwAdlerExpected != 0xFFFFFFFF) + { + dwAdlerValue = adler32(0, pbInSector, dwRawBytesInThisSector); + if(dwAdlerValue != dwAdlerExpected) + { + nError = ERROR_CHECKSUM_ERROR; + break; + } + } + } + + // If the sector is really compressed, decompress it. + // WARNING : Some sectors may not be compressed, it can be determined only + // by comparing uncompressed and compressed size !!! + if(dwRawBytesInThisSector < dwBytesInThisSector) + { + int cbOutSector = dwBytesInThisSector; + int cbInSector = dwRawBytesInThisSector; + int nResult = 0; + + // Is the file compressed by Blizzard's multiple compression ? + if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS) + { + // Remember the last used compression + hf->dwCompression0 = pbInSector[0]; + + // Decompress the data + if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2) + nResult = SCompDecompress2(pbOutSector, &cbOutSector, pbInSector, cbInSector); + else + nResult = SCompDecompress(pbOutSector, &cbOutSector, pbInSector, cbInSector); + } + + // Is the file compressed by PKWARE Data Compression Library ? + else if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE) + { + nResult = SCompExplode(pbOutSector, &cbOutSector, pbInSector, cbInSector); + } + + // Did the decompression fail ? + if(nResult == 0) + { + nError = ERROR_FILE_CORRUPT; + break; + } + } + else + { + if(pbOutSector != pbInSector) + memcpy(pbOutSector, pbInSector, dwBytesInThisSector); + } + + // Move pointers + dwBytesToRead -= dwBytesInThisSector; + dwByteOffset += dwBytesInThisSector; + dwBytesRead += dwBytesInThisSector; + pbOutSector += dwBytesInThisSector; + pbInSector += dwRawBytesInThisSector; + dwSectorsDone++; + } + } + else + { + nError = GetLastError(); + } + + // Free all used buffers + if(pbRawSector != NULL) + STORM_FREE(pbRawSector); + + // Give the caller thenumber of bytes read + *pdwBytesRead = dwBytesRead; + return nError; +} + +static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead) +{ + ULONGLONG RawFilePos = hf->RawFilePos; + TMPQArchive * ha = hf->ha; + TFileEntry * pFileEntry = hf->pFileEntry; + LPBYTE pbCompressed = NULL; + LPBYTE pbRawData; + int nError = ERROR_SUCCESS; + + // If the file buffer is not allocated yet, do it. + if(hf->pbFileSector == NULL) + { + nError = AllocateSectorBuffer(hf); + if(nError != ERROR_SUCCESS || hf->pbFileSector == NULL) + return nError; + } + + // If the file is a patch file, adjust raw data offset + if(hf->pPatchInfo != NULL) + RawFilePos += hf->pPatchInfo->dwLength; + pbRawData = hf->pbFileSector; + + // If the file sector is not loaded yet, do it + if(hf->dwSectorOffs != 0) + { + // Is the file compressed? + if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) + { + // Allocate space for compressed data + pbCompressed = STORM_ALLOC(BYTE, pFileEntry->dwCmpSize); + if(pbCompressed == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + pbRawData = pbCompressed; + } + + // Load the raw (compressed, encrypted) data + if(!FileStream_Read(ha->pStream, &RawFilePos, pbRawData, pFileEntry->dwCmpSize)) + { + STORM_FREE(pbCompressed); + return GetLastError(); + } + + // If the file is encrypted, we have to decrypt the data first + if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) + { + BSWAP_ARRAY32_UNSIGNED(pbRawData, pFileEntry->dwCmpSize); + DecryptMpqBlock(pbRawData, pFileEntry->dwCmpSize, hf->dwFileKey); + BSWAP_ARRAY32_UNSIGNED(pbRawData, pFileEntry->dwCmpSize); + } + + // If the file is compressed, we have to decompress it now + if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) + { + int cbOutBuffer = (int)hf->dwDataSize; + int cbInBuffer = (int)pFileEntry->dwCmpSize; + int nResult = 0; + + // + // If the file is an incremental patch, the size of compressed data + // is determined as pFileEntry->dwCmpSize - sizeof(TPatchInfo) + // + // In "wow-update-12694.MPQ" from Wow-Cataclysm BETA: + // + // File CmprSize DcmpSize DataSize Compressed? + // -------------------------------------- ---------- -------- -------- --------------- + // esES\DBFilesClient\LightSkyBox.dbc 0xBE->0xA2 0xBC 0xBC Yes + // deDE\DBFilesClient\MountCapability.dbc 0x93->0x77 0x77 0x77 No + // + + if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) + cbInBuffer = cbInBuffer - sizeof(TPatchInfo); + + // Is the file compressed by Blizzard's multiple compression ? + if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS) + { + // Remember the last used compression + hf->dwCompression0 = pbRawData[0]; + + // Decompress the file + if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2) + nResult = SCompDecompress2(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer); + else + nResult = SCompDecompress(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer); + } + + // Is the file compressed by PKWARE Data Compression Library ? + // Note: Single unit files compressed with IMPLODE are not supported by Blizzard + else if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE) + nResult = SCompExplode(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer); + + nError = (nResult != 0) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT; + } + else + { + if(hf->pbFileSector != NULL && pbRawData != hf->pbFileSector) + memcpy(hf->pbFileSector, pbRawData, hf->dwDataSize); + } + + // Free the decompression buffer. + if(pbCompressed != NULL) + STORM_FREE(pbCompressed); + + // The file sector is now properly loaded + hf->dwSectorOffs = 0; + } + + // At this moment, we have the file loaded into the file buffer. + // Copy as much as the caller wants + if(nError == ERROR_SUCCESS && hf->dwSectorOffs == 0) + { + // File position is greater or equal to file size ? + if(dwFilePos >= hf->dwDataSize) + { + *pdwBytesRead = 0; + return ERROR_SUCCESS; + } + + // If not enough bytes remaining in the file, cut them + if((hf->dwDataSize - dwFilePos) < dwToRead) + dwToRead = (hf->dwDataSize - dwFilePos); + + // Copy the bytes + memcpy(pvBuffer, hf->pbFileSector + dwFilePos, dwToRead); + + // Give the number of bytes read + *pdwBytesRead = dwToRead; + } + + // An error, sorry + return nError; +} + +static int ReadMpkFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead) +{ + ULONGLONG RawFilePos = hf->RawFilePos + 0x0C; // For some reason, MPK files start at position (hf->RawFilePos + 0x0C) + TMPQArchive * ha = hf->ha; + TFileEntry * pFileEntry = hf->pFileEntry; + LPBYTE pbCompressed = NULL; + LPBYTE pbRawData = hf->pbFileSector; + int nError = ERROR_SUCCESS; + + // We do not support patch files in MPK archives + assert(hf->pPatchInfo == NULL); + + // If the file buffer is not allocated yet, do it. + if(hf->pbFileSector == NULL) + { + nError = AllocateSectorBuffer(hf); + if(nError != ERROR_SUCCESS || hf->pbFileSector == NULL) + return nError; + pbRawData = hf->pbFileSector; + } + + // If the file sector is not loaded yet, do it + if(hf->dwSectorOffs != 0) + { + // Is the file compressed? + if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) + { + // Allocate space for compressed data + pbCompressed = STORM_ALLOC(BYTE, pFileEntry->dwCmpSize); + if(pbCompressed == NULL) + return ERROR_NOT_ENOUGH_MEMORY; + pbRawData = pbCompressed; + } + + // Load the raw (compressed, encrypted) data + if(!FileStream_Read(ha->pStream, &RawFilePos, pbRawData, pFileEntry->dwCmpSize)) + { + STORM_FREE(pbCompressed); + return GetLastError(); + } + + // If the file is encrypted, we have to decrypt the data first + if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) + { + DecryptMpkTable(pbRawData, pFileEntry->dwCmpSize); + } + + // If the file is compressed, we have to decompress it now + if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) + { + int cbOutBuffer = (int)hf->dwDataSize; + + hf->dwCompression0 = pbRawData[0]; + if(!SCompDecompressMpk(hf->pbFileSector, &cbOutBuffer, pbRawData, (int)pFileEntry->dwCmpSize)) + nError = ERROR_FILE_CORRUPT; + } + else + { + if(pbRawData != hf->pbFileSector) + memcpy(hf->pbFileSector, pbRawData, hf->dwDataSize); + } + + // Free the decompression buffer. + if(pbCompressed != NULL) + STORM_FREE(pbCompressed); + + // The file sector is now properly loaded + hf->dwSectorOffs = 0; + } + + // At this moment, we have the file loaded into the file buffer. + // Copy as much as the caller wants + if(nError == ERROR_SUCCESS && hf->dwSectorOffs == 0) + { + // File position is greater or equal to file size ? + if(dwFilePos >= hf->dwDataSize) + { + *pdwBytesRead = 0; + return ERROR_SUCCESS; + } + + // If not enough bytes remaining in the file, cut them + if((hf->dwDataSize - dwFilePos) < dwToRead) + dwToRead = (hf->dwDataSize - dwFilePos); + + // Copy the bytes + memcpy(pvBuffer, hf->pbFileSector + dwFilePos, dwToRead); + + // Give the number of bytes read + *pdwBytesRead = dwToRead; + return ERROR_SUCCESS; + } + + // An error, sorry + return ERROR_CAN_NOT_COMPLETE; +} + + +static int ReadMpqFileSectorFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwBytesToRead, LPDWORD pdwBytesRead) +{ + TMPQArchive * ha = hf->ha; + LPBYTE pbBuffer = (BYTE *)pvBuffer; + DWORD dwTotalBytesRead = 0; // Total bytes read in all three parts + DWORD dwSectorSizeMask = ha->dwSectorSize - 1; // Mask for block size, usually 0x0FFF + DWORD dwFileSectorPos; // File offset of the loaded sector + DWORD dwBytesRead; // Number of bytes read (temporary variable) + int nError; + + // If the file position is at or beyond end of file, do nothing + if(dwFilePos >= hf->dwDataSize) + { + *pdwBytesRead = 0; + return ERROR_SUCCESS; + } + + // If not enough bytes in the file remaining, cut them + if(dwBytesToRead > (hf->dwDataSize - dwFilePos)) + dwBytesToRead = (hf->dwDataSize - dwFilePos); + + // Compute sector position in the file + dwFileSectorPos = dwFilePos & ~dwSectorSizeMask; // Position in the block + + // If the file sector buffer is not allocated yet, do it now + if(hf->pbFileSector == NULL) + { + nError = AllocateSectorBuffer(hf); + if(nError != ERROR_SUCCESS || hf->pbFileSector == NULL) + return nError; + } + + // Load the first (incomplete) file sector + if(dwFilePos & dwSectorSizeMask) + { + DWORD dwBytesInSector = ha->dwSectorSize; + DWORD dwBufferOffs = dwFilePos & dwSectorSizeMask; + DWORD dwToCopy; + + // Is the file sector already loaded ? + if(hf->dwSectorOffs != dwFileSectorPos) + { + // Load one MPQ sector into archive buffer + nError = ReadMpqSectors(hf, hf->pbFileSector, dwFileSectorPos, ha->dwSectorSize, &dwBytesInSector); + if(nError != ERROR_SUCCESS) + return nError; + + // Remember that the data loaded to the sector have new file offset + hf->dwSectorOffs = dwFileSectorPos; + } + else + { + if((dwFileSectorPos + dwBytesInSector) > hf->dwDataSize) + dwBytesInSector = hf->dwDataSize - dwFileSectorPos; + } + + // Copy the data from the offset in the loaded sector to the end of the sector + dwToCopy = dwBytesInSector - dwBufferOffs; + if(dwToCopy > dwBytesToRead) + dwToCopy = dwBytesToRead; + + // Copy data from sector buffer into target buffer + memcpy(pbBuffer, hf->pbFileSector + dwBufferOffs, dwToCopy); + + // Update pointers and byte counts + dwTotalBytesRead += dwToCopy; + dwFileSectorPos += dwBytesInSector; + pbBuffer += dwToCopy; + dwBytesToRead -= dwToCopy; + } + + // Load the whole ("middle") sectors only if there is at least one full sector to be read + if(dwBytesToRead >= ha->dwSectorSize) + { + DWORD dwBlockBytes = dwBytesToRead & ~dwSectorSizeMask; + + // Load all sectors to the output buffer + nError = ReadMpqSectors(hf, pbBuffer, dwFileSectorPos, dwBlockBytes, &dwBytesRead); + if(nError != ERROR_SUCCESS) + return nError; + + // Update pointers + dwTotalBytesRead += dwBytesRead; + dwFileSectorPos += dwBytesRead; + pbBuffer += dwBytesRead; + dwBytesToRead -= dwBytesRead; + } + + // Read the terminating sector + if(dwBytesToRead > 0) + { + DWORD dwToCopy = ha->dwSectorSize; + + // Is the file sector already loaded ? + if(hf->dwSectorOffs != dwFileSectorPos) + { + // Load one MPQ sector into archive buffer + nError = ReadMpqSectors(hf, hf->pbFileSector, dwFileSectorPos, ha->dwSectorSize, &dwBytesRead); + if(nError != ERROR_SUCCESS) + return nError; + + // Remember that the data loaded to the sector have new file offset + hf->dwSectorOffs = dwFileSectorPos; + } + + // Check number of bytes read + if(dwToCopy > dwBytesToRead) + dwToCopy = dwBytesToRead; + + // Copy the data from the cached last sector to the caller's buffer + memcpy(pbBuffer, hf->pbFileSector, dwToCopy); + + // Update pointers + dwTotalBytesRead += dwToCopy; + } + + // Store total number of bytes read to the caller + *pdwBytesRead = dwTotalBytesRead; + return ERROR_SUCCESS; +} + +static int ReadMpqFilePatchFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead) +{ + TMPQPatcher Patcher; + DWORD dwBytesToRead = dwToRead; + DWORD dwBytesRead = 0; + int nError = ERROR_SUCCESS; + + // Make sure that the patch file is loaded completely + if(nError == ERROR_SUCCESS && hf->pbFileData == NULL) + { + // Initialize patching process and allocate data + nError = Patch_InitPatcher(&Patcher, hf); + if(nError != ERROR_SUCCESS) + return nError; + + // Set the current data size + Patcher.cbFileData = hf->pFileEntry->dwFileSize; + + // Initialize the patcher object with initial file data + if(hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) + nError = ReadMpqFileSingleUnit(hf, Patcher.pbFileData1, 0, Patcher.cbFileData, &dwBytesRead); + else + nError = ReadMpqFileSectorFile(hf, Patcher.pbFileData1, 0, Patcher.cbFileData, &dwBytesRead); + + // Perform the patching process + if(nError == ERROR_SUCCESS) + nError = Patch_Process(&Patcher, hf); + + // Finalize the patcher structure + Patch_Finalize(&Patcher); + dwBytesRead = 0; + } + + // If there is something to read, do it + if(nError == ERROR_SUCCESS) + { + if(dwFilePos < hf->cbFileData) + { + // Make sure we don't copy more than file size + if((dwFilePos + dwToRead) > hf->cbFileData) + dwToRead = hf->cbFileData - dwFilePos; + + // Copy the appropriate amount of the file data to the caller's buffer + memcpy(pvBuffer, hf->pbFileData + dwFilePos, dwToRead); + dwBytesRead = dwToRead; + } + + // Set the proper error code + nError = (dwBytesRead == dwBytesToRead) ? ERROR_SUCCESS : ERROR_HANDLE_EOF; + } + + // Give the result to the caller + if(pdwBytesRead != NULL) + *pdwBytesRead = dwBytesRead; + return nError; +} + +static int ReadMpqFileLocalFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead) +{ + ULONGLONG FilePosition1 = dwFilePos; + ULONGLONG FilePosition2; + DWORD dwBytesRead = 0; + int nError = ERROR_SUCCESS; + + assert(hf->pStream != NULL); + + // Because stream I/O functions are designed to read + // "all or nothing", we compare file position before and after, + // and if they differ, we assume that number of bytes read + // is the difference between them + + if(!FileStream_Read(hf->pStream, &FilePosition1, pvBuffer, dwToRead)) + { + // If not all bytes have been read, then return the number of bytes read + if((nError = GetLastError()) == ERROR_HANDLE_EOF) + { + FileStream_GetPos(hf->pStream, &FilePosition2); + dwBytesRead = (DWORD)(FilePosition2 - FilePosition1); + } + } + else + { + dwBytesRead = dwToRead; + } + + *pdwBytesRead = dwBytesRead; + return nError; +} + +//----------------------------------------------------------------------------- +// SFileReadFile + +bool WINAPI SFileReadFile(HANDLE hFile, void * pvBuffer, DWORD dwToRead, LPDWORD pdwRead, LPOVERLAPPED lpOverlapped) +{ + TMPQFile * hf = (TMPQFile *)hFile; + DWORD dwBytesRead = 0; // Number of bytes read + int nError = ERROR_SUCCESS; + + // Always zero the result + if(pdwRead != NULL) + *pdwRead = 0; + lpOverlapped = lpOverlapped; + + // Check valid parameters + if(!IsValidFileHandle(hFile)) + { + SetLastError(ERROR_INVALID_HANDLE); + return false; + } + + if(pvBuffer == NULL) + { + SetLastError(ERROR_INVALID_PARAMETER); + return false; + } + + // If we didn't load the patch info yet, do it now + if(hf->pFileEntry != NULL && (hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) && hf->pPatchInfo == NULL) + { + nError = AllocatePatchInfo(hf, true); + if(nError != ERROR_SUCCESS || hf->pPatchInfo == NULL) + { + SetLastError(nError); + return false; + } + } + + // Clear the last used compression + hf->dwCompression0 = 0; + + // If the file is local file, read the data directly from the stream + if(hf->pStream != NULL) + { + nError = ReadMpqFileLocalFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); + } + + // If the file is a patch file, we have to read it special way + else if(hf->hfPatch != NULL && (hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0) + { + nError = ReadMpqFilePatchFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); + } + + // If the archive is a MPK archive, we need special way to read the file + else if(hf->ha->dwSubType == MPQ_SUBTYPE_MPK) + { + nError = ReadMpkFileSingleUnit(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); + } + + // If the file is single unit file, redirect it to read file + else if(hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) + { + nError = ReadMpqFileSingleUnit(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); + } + + // Otherwise read it as sector based MPQ file + else + { + nError = ReadMpqFileSectorFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); + } + + // Increment the file position + hf->dwFilePos += dwBytesRead; + + // Give the caller the number of bytes read + if(pdwRead != NULL) + *pdwRead = dwBytesRead; + + // If the read operation succeeded, but not full number of bytes was read, + // set the last error to ERROR_HANDLE_EOF + if(nError == ERROR_SUCCESS && (dwBytesRead < dwToRead)) + nError = ERROR_HANDLE_EOF; + + // If something failed, set the last error value + if(nError != ERROR_SUCCESS) + SetLastError(nError); + return (nError == ERROR_SUCCESS); +} + +//----------------------------------------------------------------------------- +// SFileGetFileSize + +DWORD WINAPI SFileGetFileSize(HANDLE hFile, LPDWORD pdwFileSizeHigh) +{ + ULONGLONG FileSize; + TMPQFile * hf = (TMPQFile *)hFile; + + // Validate the file handle before we go on + if(IsValidFileHandle(hFile)) + { + // Make sure that the variable is initialized + FileSize = 0; + + // If the file is patched file, we have to get the size of the last version + if(hf->hfPatch != NULL) + { + // Walk through the entire patch chain, take the last version + while(hf != NULL) + { + // Get the size of the currently pointed version + FileSize = hf->pFileEntry->dwFileSize; + + // Move to the next patch file in the hierarchy + hf = hf->hfPatch; + } + } + else + { + // Is it a local file ? + if(hf->pStream != NULL) + { + FileStream_GetSize(hf->pStream, &FileSize); + } + else + { + FileSize = hf->dwDataSize; + } + } + + // If opened from archive, return file size + if(pdwFileSizeHigh != NULL) + *pdwFileSizeHigh = (DWORD)(FileSize >> 32); + return (DWORD)FileSize; + } + + SetLastError(ERROR_INVALID_HANDLE); + return SFILE_INVALID_SIZE; +} + +DWORD WINAPI SFileSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod) +{ + TMPQFile * hf = (TMPQFile *)hFile; + ULONGLONG OldPosition; + ULONGLONG NewPosition; + ULONGLONG FileSize; + ULONGLONG DeltaPos; + + // If the hFile is not a valid file handle, return an error. + if(!IsValidFileHandle(hFile)) + { + SetLastError(ERROR_INVALID_HANDLE); + return SFILE_INVALID_POS; + } + + // Retrieve the file size for handling the limits + if(hf->pStream != NULL) + { + FileStream_GetSize(hf->pStream, &FileSize); + } + else + { + FileSize = SFileGetFileSize(hFile, NULL); + } + + // Handle the NULL and non-NULL values of plFilePosHigh + // Non-NULL: The DeltaPos is combined from lFilePos and *lpFilePosHigh + // NULL: The DeltaPos is sign-extended value of lFilePos + DeltaPos = (plFilePosHigh != NULL) ? MAKE_OFFSET64(plFilePosHigh[0], lFilePos) : (ULONGLONG)(LONGLONG)lFilePos; + + // Get the relative point where to move from + switch(dwMoveMethod) + { + case FILE_BEGIN: + + // Move relative to the file begin. + OldPosition = 0; + break; + + case FILE_CURRENT: + + // Retrieve the current file position + if(hf->pStream != NULL) + { + FileStream_GetPos(hf->pStream, &OldPosition); + } + else + { + OldPosition = hf->dwFilePos; + } + break; + + case FILE_END: + + // Move relative to the end of the file + OldPosition = FileSize; + break; + + default: + SetLastError(ERROR_INVALID_PARAMETER); + return SFILE_INVALID_POS; + } + + // Calculate the new position + NewPosition = OldPosition + DeltaPos; + + // If moving backward, don't allow the new position go negative + if((LONGLONG)DeltaPos < 0) + { + if(NewPosition > FileSize) // Position is negative + { + SetLastError(ERROR_NEGATIVE_SEEK); + return SFILE_INVALID_POS; + } + } + + // If moving forward, don't allow the new position go past the end of the file + else + { + if(NewPosition > FileSize) + NewPosition = FileSize; + } + + // Now apply the file pointer to the file + if(hf->pStream != NULL) + { + if(!FileStream_Read(hf->pStream, &NewPosition, NULL, 0)) + return SFILE_INVALID_POS; + } + + // Also, store the new file position to the TMPQFile struct + hf->dwFilePos = (DWORD)NewPosition; + + // Return the new file position + if(plFilePosHigh != NULL) + *plFilePosHigh = (LONG)(NewPosition >> 32); + return (DWORD)NewPosition; +} diff --git a/3rdParty/StormLib/src/StormCommon.h b/3rdParty/StormLib/src/StormCommon.h index 08ab6620b..f8023c417 100644 --- a/3rdParty/StormLib/src/StormCommon.h +++ b/3rdParty/StormLib/src/StormCommon.h @@ -18,9 +18,8 @@ // Compression support // Include functions from Pkware Data Compression Library -#include "../../PKWare/pkware.h" +#include "pklib/pklib.h" -#ifdef FULL // Include functions from Huffmann compression #include "huffman/huff.h" @@ -56,7 +55,6 @@ // For HashStringJenkins #include "jenkins/lookup.h" -#endif //----------------------------------------------------------------------------- // StormLib private defines @@ -72,6 +70,18 @@ // Macro for building 64-bit file offset from two 32-bit #define MAKE_OFFSET64(hi, lo) (((ULONGLONG)hi << 32) | (ULONGLONG)lo) +//----------------------------------------------------------------------------- +// MTYPE definition - specifies what kind of MPQ is the map type + +typedef enum _MTYPE +{ + MapTypeNotChecked, // The map type was not checked yet + MapTypeNotRecognized, // The file does not seems to be a map + MapTypeAviFile, // The file is actually an AVI file (Warcraft III cinematics) + MapTypeWarcraft3, // The file is a Warcraft III map + MapTypeStarcraft2 // The file is a Starcraft II map +} MTYPE, *PMTYPE; + //----------------------------------------------------------------------------- // MPQ signature information @@ -124,7 +134,10 @@ typedef struct _MPQ_SIGNATURE_INFO //----------------------------------------------------------------------------- // StormLib internal global variables -extern LCID lcFileLocale; // Preferred file locale +extern DWORD g_dwMpqSignature; // Marker for MPQ header +extern DWORD g_dwHashTableKey; // Key for hash table +extern DWORD g_dwBlockTableKey; // Key for block table +extern LCID g_lcFileLocale; // Preferred file locale //----------------------------------------------------------------------------- // Conversion to uppercase/lowercase (and "/" to "\") @@ -135,8 +148,46 @@ extern unsigned char AsciiToUpperTable[256]; //----------------------------------------------------------------------------- // Safe string functions -void StringCopy(char * szTarget, size_t cchTarget, const char * szSource); +template +XCHAR * IntToString(XCHAR * szBuffer, size_t cchMaxChars, XINT nValue, size_t nDigitCount = 0) +{ + XCHAR * szBufferEnd = szBuffer + cchMaxChars - 1; + XCHAR szNumberRev[0x20]; + size_t nLength = 0; + + // Always put the first digit + szNumberRev[nLength++] = (XCHAR)(nValue % 10) + '0'; + nValue /= 10; + + // Continue as long as we have non-zero + while(nValue != 0) + { + szNumberRev[nLength++] = (XCHAR)(nValue % 10) + '0'; + nValue /= 10; + } + + // Fill zeros, if needed + while(szBuffer < szBufferEnd && nLength < nDigitCount) + { + *szBuffer++ = '0'; + nDigitCount--; + } + + // Fill the buffer + while(szBuffer < szBufferEnd && nLength > 0) + { + nLength--; + *szBuffer++ = szNumberRev[nLength]; + } + + // Terminate the number with zeros + szBuffer[0] = 0; + return szBuffer; +} + +char * StringCopy(char * szTarget, size_t cchTarget, const char * szSource); void StringCat(char * szTarget, size_t cchTargetMax, const char * szSource); +void StringCreatePseudoFileName(char * szBuffer, size_t cchMaxChars, unsigned int nIndex, const char * szExtension); #ifdef _UNICODE void StringCopy(TCHAR * szTarget, size_t cchTarget, const char * szSource); @@ -191,7 +242,7 @@ TMPQFile * IsValidFileHandle(HANDLE hFile); ULONGLONG FileOffsetFromMpqOffset(TMPQArchive * ha, ULONGLONG MpqOffset); ULONGLONG CalculateRawSectorOffset(TMPQFile * hf, DWORD dwSectorOffset); -int ConvertMpqHeaderToFormat4(TMPQArchive * ha, ULONGLONG MpqOffset, ULONGLONG FileSize, DWORD dwFlags, bool bIsWarcraft3Map); +int ConvertMpqHeaderToFormat4(TMPQArchive * ha, ULONGLONG MpqOffset, ULONGLONG FileSize, DWORD dwFlags, MTYPE MapType); bool IsValidHashEntry(TMPQArchive * ha, TMPQHash * pHash); @@ -263,7 +314,7 @@ int SCompDecompressMpk(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer TMPQFile * CreateFileHandle(TMPQArchive * ha, TFileEntry * pFileEntry); TMPQFile * CreateWritableHandle(TMPQArchive * ha, DWORD dwFileSize); -void * LoadMpqTable(TMPQArchive * ha, ULONGLONG ByteOffset, DWORD dwCompressedSize, DWORD dwRealSize, DWORD dwKey, bool * pbTableIsCut); +void * LoadMpqTable(TMPQArchive * ha, ULONGLONG ByteOffset, LPBYTE pbTableHash, DWORD dwCompressedSize, DWORD dwRealSize, DWORD dwKey, bool * pbTableIsCut); int AllocateSectorBuffer(TMPQFile * hf); int AllocatePatchInfo(TMPQFile * hf, bool bLoadFromFile); int AllocateSectorOffsets(TMPQFile * hf, bool bLoadFromFile); @@ -299,7 +350,6 @@ void Patch_Finalize(TMPQPatcher * pPatcher); //----------------------------------------------------------------------------- // Utility functions -bool CheckWildCard(const char * szString, const char * szWildCard); bool IsInternalMpqFileName(const char * szFileName); template diff --git a/3rdParty/StormLib/src/StormLib.h b/3rdParty/StormLib/src/StormLib.h index 86cd8e2c4..145cf58c7 100644 --- a/3rdParty/StormLib/src/StormLib.h +++ b/3rdParty/StormLib/src/StormLib.h @@ -79,9 +79,9 @@ #define __STORMLIB_H__ #ifdef _MSC_VER -#pragma warning(disable:4668) // 'XXX' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' +#pragma warning(disable:4668) // 'XXX' is not defined as a preprocessor macro, replacing with '0' for '#if/#elif' #pragma warning(disable:4820) // 'XXX' : '2' bytes padding added after data member 'XXX::yyy' -#endif +#endif #include "StormPort.h" @@ -94,7 +94,7 @@ extern "C" { // // The library type is encoded in the library name as the following // StormLibXYZ.lib -// +// // X - D for Debug version, R for Release version // Y - A for ANSI version, U for Unicode version // Z - S for static-linked CRT library, D for multithreaded DLL CRT library @@ -105,32 +105,34 @@ extern "C" { #endif #if defined(_MSC_VER) && !defined(STORMLIB_NO_AUTO_LINK) - #ifdef _DEBUG // DEBUG VERSIONS - #ifndef _UNICODE - #ifdef _DLL - #pragma comment(lib, "StormLibDAD.lib") // Debug Ansi CRT-DLL version - #else - #pragma comment(lib, "StormLibDAS.lib") // Debug Ansi CRT-LIB version - #endif - #else - #ifdef _DLL - #pragma comment(lib, "StormLibDUD.lib") // Debug Unicode CRT-DLL version - #else - #pragma comment(lib, "StormLibDUS.lib") // Debug Unicode CRT-LIB version - #endif - #endif - #else // RELEASE VERSIONS - #ifndef _UNICODE - #ifdef _DLL - #pragma comment(lib, "StormLibRAD.lib") // Release Ansi CRT-DLL version - #else - #pragma comment(lib, "StormLibRAS.lib") // Release Ansi CRT-LIB version + #ifndef WDK_BUILD + #ifdef _DEBUG // DEBUG VERSIONS + #ifndef _UNICODE + #ifdef _DLL + #pragma comment(lib, "StormLibDAD.lib") // Debug Ansi CRT-DLL version + #else + #pragma comment(lib, "StormLibDAS.lib") // Debug Ansi CRT-LIB version + #endif + #else + #ifdef _DLL + #pragma comment(lib, "StormLibDUD.lib") // Debug Unicode CRT-DLL version + #else + #pragma comment(lib, "StormLibDUS.lib") // Debug Unicode CRT-LIB version + #endif #endif - #else - #ifdef _DLL - #pragma comment(lib, "StormLibRUD.lib") // Release Unicode CRT-DLL version - #else - #pragma comment(lib, "StormLibRUS.lib") // Release Unicode CRT-LIB version + #else // RELEASE VERSIONS + #ifndef _UNICODE + #ifdef _DLL + #pragma comment(lib, "StormLibRAD.lib") // Release Ansi CRT-DLL version + #else + #pragma comment(lib, "StormLibRAS.lib") // Release Ansi CRT-LIB version + #endif + #else + #ifdef _DLL + #pragma comment(lib, "StormLibRUD.lib") // Release Unicode CRT-DLL version + #else + #pragma comment(lib, "StormLibRUS.lib") // Release Unicode CRT-LIB version + #endif #endif #endif #endif @@ -140,8 +142,8 @@ extern "C" { //----------------------------------------------------------------------------- // Defines -#define STORMLIB_VERSION 0x0916 // Current version of StormLib (9.21) -#define STORMLIB_VERSION_STRING "9.22" // String version of StormLib version +#define STORMLIB_VERSION 0x0917 // Current version of StormLib (9.23) +#define STORMLIB_VERSION_STRING "9.23" // String version of StormLib version #define ID_MPQ 0x1A51504D // MPQ archive header ID ('MPQ\x1A') #define ID_MPQ_USERDATA 0x1B51504D // MPQ userdata entry ('MPQ\x1B') @@ -156,6 +158,7 @@ extern "C" { #define ERROR_FILE_INCOMPLETE 10006 // The required file part is missing #define ERROR_UNKNOWN_FILE_NAMES 10007 // A name of at least one file is unknown #define ERROR_CANT_FIND_PATCH_PREFIX 10008 // StormLib was unable to find patch prefix for the patches +#define ERROR_FAKE_MPQ_HEADER 10009 // The header at this position is fake header // Values for SFileCreateArchive #define HASH_TABLE_SIZE_MIN 0x00000004 // Verified: If there is 1 file, hash table size is 4 @@ -181,7 +184,7 @@ extern "C" { #define SFILE_OPEN_ANY_LOCALE 0xFFFFFFFE // Reserved for StormLib internal use #define SFILE_OPEN_LOCAL_FILE 0xFFFFFFFF // Open a local file -// Flags for TMPQArchive::dwFlags +// Flags for TMPQArchive::dwFlags. Used internally #define MPQ_FLAG_READ_ONLY 0x00000001 // If set, the MPQ has been open for read-only access #define MPQ_FLAG_CHANGED 0x00000002 // If set, the MPQ tables have been changed #define MPQ_FLAG_MALFORMED 0x00000004 // Malformed data structure detected (W3M map protectors) @@ -193,10 +196,11 @@ extern "C" { #define MPQ_FLAG_WAR3_MAP 0x00000100 // If set, this MPQ is a map for Warcraft III #define MPQ_FLAG_LISTFILE_NONE 0x00000200 // Set when no (listfile) was found in InvalidateInternalFiles #define MPQ_FLAG_LISTFILE_NEW 0x00000400 // Set when (listfile) invalidated by InvalidateInternalFiles -#define MPQ_FLAG_ATTRIBUTES_NONE 0x00000800 // Set when no (attributes) was found in InvalidateInternalFiles -#define MPQ_FLAG_ATTRIBUTES_NEW 0x00001000 // Set when (attributes) invalidated by InvalidateInternalFiles -#define MPQ_FLAG_SIGNATURE_NONE 0x00002000 // Set when no (signature) was found in InvalidateInternalFiles -#define MPQ_FLAG_SIGNATURE_NEW 0x00004000 // Set when (signature) invalidated by InvalidateInternalFiles +#define MPQ_FLAG_LISTFILE_FORCE 0x00000800 // Save updated listfile on exit +#define MPQ_FLAG_ATTRIBUTES_NONE 0x00001000 // Set when no (attributes) was found in InvalidateInternalFiles +#define MPQ_FLAG_ATTRIBUTES_NEW 0x00002000 // Set when (attributes) invalidated by InvalidateInternalFiles +#define MPQ_FLAG_SIGNATURE_NONE 0x00004000 // Set when no (signature) was found in InvalidateInternalFiles +#define MPQ_FLAG_SIGNATURE_NEW 0x00008000 // Set when (signature) invalidated by InvalidateInternalFiles // Values for TMPQArchive::dwSubType #define MPQ_SUBTYPE_MPQ 0x00000000 // The file is a MPQ file (Blizzard games) @@ -211,7 +215,7 @@ extern "C" { // Flags for SFileAddFile #define MPQ_FILE_IMPLODE 0x00000100 // Implode method (By PKWARE Data Compression Library) #define MPQ_FILE_COMPRESS 0x00000200 // Compress methods (By multiple methods) -#define MPQ_FILE_ENCRYPTED 0x00010000 // Indicates whether file is encrypted +#define MPQ_FILE_ENCRYPTED 0x00010000 // Indicates whether file is encrypted #define MPQ_FILE_FIX_KEY 0x00020000 // File decryption key has to be fixed #define MPQ_FILE_PATCH_FILE 0x00100000 // The file is a patch file. Raw file data begin with TPatchInfo structure #define MPQ_FILE_SINGLE_UNIT 0x01000000 // File is stored as a single unit, rather than split into sectors (Thx, Quantam) @@ -283,7 +287,7 @@ extern "C" { #define PATCH_METADATA_NAME "(patch_metadata)" #define MPQ_FORMAT_VERSION_1 0 // Up to The Burning Crusade -#define MPQ_FORMAT_VERSION_2 1 // The Burning Crusade and newer +#define MPQ_FORMAT_VERSION_2 1 // The Burning Crusade and newer #define MPQ_FORMAT_VERSION_3 2 // WoW Cataclysm Beta #define MPQ_FORMAT_VERSION_4 3 // WoW Cataclysm and newer @@ -322,6 +326,7 @@ extern "C" { #define MPQ_OPEN_FORCE_MPQ_V1 0x00080000 // Always open the archive as MPQ v 1.00, ignore the "wFormatVersion" variable in the header #define MPQ_OPEN_CHECK_SECTOR_CRC 0x00100000 // On files with MPQ_FILE_SECTOR_CRC, the CRC will be checked when reading file #define MPQ_OPEN_PATCH 0x00200000 // This archive is a patch MPQ. Used internally. +#define MPQ_OPEN_FORCE_LISTFILE 0x00400000 // Force add listfile even if there is none at the moment of opening #define MPQ_OPEN_READ_ONLY STREAM_FLAG_READ_ONLY // Flags for SFileCreateArchive @@ -377,7 +382,7 @@ extern "C" { #define ERROR_WEAK_SIGNATURE_ERROR 3 // There is a weak signature but sign check failed #define ERROR_STRONG_SIGNATURE_OK 4 // There is a strong signature and sign check passed #define ERROR_STRONG_SIGNATURE_ERROR 5 // There is a strong signature but sign check failed - + #ifndef MD5_DIGEST_SIZE #define MD5_DIGEST_SIZE 0x10 #endif @@ -469,25 +474,13 @@ typedef enum _SFileInfoClass #define CCB_COPYING_NON_MPQ_DATA 3 // Copying non-MPQ data: No params used #define CCB_COMPACTING_FILES 4 // Compacting archive (dwParam1 = current, dwParam2 = total) #define CCB_CLOSING_ARCHIVE 5 // Closing archive: No params used - -typedef void (STORMAPI * SFILE_DOWNLOAD_CALLBACK)(void * pvUserData, ULONGLONG ByteOffset, DWORD dwTotalBytes); -typedef void (STORMAPI * SFILE_ADDFILE_CALLBACK)(void * pvUserData, DWORD dwBytesWritten, DWORD dwTotalBytes, bool bFinalCall); -typedef void (STORMAPI * SFILE_COMPACT_CALLBACK)(void * pvUserData, DWORD dwWorkType, ULONGLONG BytesProcessed, ULONGLONG TotalBytes); - -typedef struct TFileStream TFileStream; - -//----------------------------------------------------------------------------- -// Structure for bit arrays used for HET and BET tables -typedef struct _TBitArray -{ - DWORD NumberOfBytes; // Total number of bytes in "Elements" - DWORD NumberOfBits; // Total number of bits that are available - BYTE Elements[1]; // Array of elements (variable length) -} TBitArray; +typedef void (WINAPI * SFILE_DOWNLOAD_CALLBACK)(void * pvUserData, ULONGLONG ByteOffset, DWORD dwTotalBytes); +typedef void (WINAPI * SFILE_ADDFILE_CALLBACK)(void * pvUserData, DWORD dwBytesWritten, DWORD dwTotalBytes, bool bFinalCall); +typedef void (WINAPI * SFILE_COMPACT_CALLBACK)(void * pvUserData, DWORD dwWorkType, ULONGLONG BytesProcessed, ULONGLONG TotalBytes); -void GetBits(TBitArray * array, unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultSize); -void SetBits(TBitArray * array, unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultSize); +struct TFileStream; +struct TMPQBits; //----------------------------------------------------------------------------- // Structures related to MPQ format @@ -552,14 +545,14 @@ typedef struct _TMPQHeader // Offset to the beginning of the hash table, relative to the beginning of the archive. DWORD dwHashTablePos; - + // Offset to the beginning of the block table, relative to the beginning of the archive. DWORD dwBlockTablePos; - + // Number of entries in the hash table. Must be a power of two, and must be less than 2^16 for // the original MoPaQ format, or less than 2^20 for the Burning Crusade format. DWORD dwHashTableSize; - + // Number of entries in the block table DWORD dwBlockTableSize; @@ -604,7 +597,7 @@ typedef struct _TMPQHeader // Size of raw data chunk to calculate MD5. // MD5 of each data chunk follows the raw file data. - DWORD dwRawChunkSize; + DWORD dwRawChunkSize; // MD5 of MPQ tables unsigned char MD5_BlockTable[MD5_DIGEST_SIZE]; // MD5 of the block table before decryption @@ -621,11 +614,11 @@ typedef struct _TMPQHash { // The hash of the file path, using method A. DWORD dwName1; - + // The hash of the file path, using method B. DWORD dwName2; -#ifdef PLATFORM_LITTLE_ENDIAN +#ifdef STORMLIB_LITTLE_ENDIAN // The language of the file. This is a Windows LANGID data type, and uses the same values. // 0 indicates the default language (American English), or that the file is language-neutral. @@ -658,16 +651,16 @@ typedef struct _TMPQBlock { // Offset of the beginning of the file, relative to the beginning of the archive. DWORD dwFilePos; - + // Compressed file size DWORD dwCSize; - + // Only valid if the block is a file; otherwise meaningless, and should be 0. // If the file is compressed, this is the size of the uncompressed file data. - DWORD dwFSize; - + DWORD dwFSize; + // Flags for the file. See MPQ_FILE_XXXX constants - DWORD dwFlags; + DWORD dwFlags; } TMPQBlock; // Patch file information, preceding the sector offset table @@ -714,14 +707,14 @@ typedef struct _TMPQHetHeader { TMPQExtHeader ExtHdr; - DWORD dwTableSize; // Size of the entire HET table, including HET_TABLE_HEADER (in bytes) - DWORD dwEntryCount; // Number of occupied entries in the HET table - DWORD dwTotalCount; // Total number of entries in the HET table - DWORD dwNameHashBitSize; // Size of the name hash entry (in bits) - DWORD dwIndexSizeTotal; // Total size of file index (in bits) - DWORD dwIndexSizeExtra; // Extra bits in the file index - DWORD dwIndexSize; // Effective size of the file index (in bits) - DWORD dwIndexTableSize; // Size of the block index subtable (in bytes) + DWORD dwTableSize; // Size of the entire HET table, including HET_TABLE_HEADER (in bytes) + DWORD dwEntryCount; // Number of occupied entries in the HET table + DWORD dwTotalCount; // Total number of entries in the HET table + DWORD dwNameHashBitSize; // Size of the name hash entry (in bits) + DWORD dwIndexSizeTotal; // Total size of file index (in bits) + DWORD dwIndexSizeExtra; // Extra bits in the file index + DWORD dwIndexSize; // Effective size of the file index (in bits) + DWORD dwIndexTableSize; // Size of the block index subtable (in bytes) } TMPQHetHeader; @@ -730,32 +723,32 @@ typedef struct _TMPQBetHeader { TMPQExtHeader ExtHdr; - DWORD dwTableSize; // Size of the entire BET table, including the header (in bytes) - DWORD dwEntryCount; // Number of entries in the BET table. Must match HET_TABLE_HEADER::dwEntryCount + DWORD dwTableSize; // Size of the entire BET table, including the header (in bytes) + DWORD dwEntryCount; // Number of entries in the BET table. Must match HET_TABLE_HEADER::dwEntryCount DWORD dwUnknown08; - DWORD dwTableEntrySize; // Size of one table entry (in bits) - DWORD dwBitIndex_FilePos; // Bit index of the file position (within the entry record) - DWORD dwBitIndex_FileSize; // Bit index of the file size (within the entry record) - DWORD dwBitIndex_CmpSize; // Bit index of the compressed size (within the entry record) - DWORD dwBitIndex_FlagIndex; // Bit index of the flag index (within the entry record) - DWORD dwBitIndex_Unknown; // Bit index of the ??? (within the entry record) - DWORD dwBitCount_FilePos; // Bit size of file position (in the entry record) - DWORD dwBitCount_FileSize; // Bit size of file size (in the entry record) - DWORD dwBitCount_CmpSize; // Bit size of compressed file size (in the entry record) - DWORD dwBitCount_FlagIndex; // Bit size of flags index (in the entry record) - DWORD dwBitCount_Unknown; // Bit size of ??? (in the entry record) - DWORD dwBitTotal_NameHash2; // Total bit size of the NameHash2 - DWORD dwBitExtra_NameHash2; // Extra bits in the NameHash2 - DWORD dwBitCount_NameHash2; // Effective size of NameHash2 (in bits) - DWORD dwNameHashArraySize; // Size of NameHash2 table, in bytes - DWORD dwFlagCount; // Number of flags in the following array + DWORD dwTableEntrySize; // Size of one table entry (in bits) + DWORD dwBitIndex_FilePos; // Bit index of the file position (within the entry record) + DWORD dwBitIndex_FileSize; // Bit index of the file size (within the entry record) + DWORD dwBitIndex_CmpSize; // Bit index of the compressed size (within the entry record) + DWORD dwBitIndex_FlagIndex; // Bit index of the flag index (within the entry record) + DWORD dwBitIndex_Unknown; // Bit index of the ??? (within the entry record) + DWORD dwBitCount_FilePos; // Bit size of file position (in the entry record) + DWORD dwBitCount_FileSize; // Bit size of file size (in the entry record) + DWORD dwBitCount_CmpSize; // Bit size of compressed file size (in the entry record) + DWORD dwBitCount_FlagIndex; // Bit size of flags index (in the entry record) + DWORD dwBitCount_Unknown; // Bit size of ??? (in the entry record) + DWORD dwBitTotal_NameHash2; // Total bit size of the NameHash2 + DWORD dwBitExtra_NameHash2; // Extra bits in the NameHash2 + DWORD dwBitCount_NameHash2; // Effective size of NameHash2 (in bits) + DWORD dwNameHashArraySize; // Size of NameHash2 table, in bytes + DWORD dwFlagCount; // Number of flags in the following array } TMPQBetHeader; // Structure for parsed HET table typedef struct _TMPQHetTable { - TBitArray * pBetIndexes; // Bit array of FileIndex values + TMPQBits * pBetIndexes; // Bit array of FileIndex values LPBYTE pNameHashes; // Array of NameHash1 values (NameHash1 = upper 8 bits of FileName hashe) ULONGLONG AndMask64; // AND mask used for calculating file name hash ULONGLONG OrMask64; // OR mask used for setting the highest bit of the file name hash @@ -771,8 +764,8 @@ typedef struct _TMPQHetTable // Structure for parsed BET table typedef struct _TMPQBetTable { - TBitArray * pNameHashes; // Array of NameHash2 entries (lower 24 bits of FileName hash) - TBitArray * pFileTable; // Bit-based file table + TMPQBits * pNameHashes; // Array of NameHash2 entries (lower 24 bits of FileName hash) + TMPQBits * pFileTable; // Bit-based file table LPDWORD pFileFlags; // Array of file flags DWORD dwTableEntrySize; // Size of one table entry, in bits @@ -832,7 +825,7 @@ typedef struct _TMPQArchive TMPQHetTable * pHetTable; // HET table TFileEntry * pFileTable; // File table HASH_STRING pfnHashString; // Hashing function that will convert the file name into hash - + TMPQUserData UserData; // MPQ user data. Valid only when ID_MPQ_USERDATA has been found DWORD HeaderData[MPQ_HEADER_DWORDS]; // Storage for MPQ header @@ -856,7 +849,7 @@ typedef struct _TMPQArchive ULONGLONG CompactBytesProcessed; // Amount of bytes that have been processed during a particular compact call ULONGLONG CompactTotalBytes; // Total amount of bytes to be compacted void * pvCompactUserData; // User data thats passed to the callback -} TMPQArchive; +} TMPQArchive; // File handle structure typedef struct _TMPQFile @@ -931,11 +924,24 @@ typedef struct _SFILE_CREATE_MPQ } SFILE_CREATE_MPQ, *PSFILE_CREATE_MPQ; +typedef struct _SFILE_MARKERS +{ + DWORD dwSize; // Size of this structure, in bytes + DWORD dwSignature; // Alternate MPQ header marker + const char * szHashTableKey; // Replacement for "(hash table)" + const char * szBlockTableKey; // Replacement for "(block table)" +} SFILE_MARKERS, *PSFILE_MARKERS; + +//----------------------------------------------------------------------------- +// TMPQBits support - functions + +void GetMPQBits(TMPQBits * pBits, unsigned int nBitPosition, unsigned int nBitLength, void * pvBuffer, int nResultByteSize); + //----------------------------------------------------------------------------- // Stream support - functions // Structure used by FileStream_GetBitmap -typedef struct _TStreamBitmap +struct TStreamBitmap { ULONGLONG StreamSize; // Size of the stream, in bytes DWORD BitmapSize; // Size of the block map, in bytes @@ -944,8 +950,7 @@ typedef struct _TStreamBitmap DWORD IsComplete; // Nonzero if the file is complete // Followed by the BYTE array, each bit means availability of one block - -} TStreamBitmap; +}; // UNICODE versions of the file access functions TFileStream * FileStream_CreateFile(const TCHAR * szFileName, DWORD dwStreamFlags); @@ -955,7 +960,7 @@ size_t FileStream_Prefix(const TCHAR * szFileName, DWORD * pdwProvider); bool FileStream_SetCallback(TFileStream * pStream, SFILE_DOWNLOAD_CALLBACK pfnCallback, void * pvUserData); -bool FileStream_GetBitmap(TFileStream * pStream, void * pvBitmap, DWORD cbBitmap, LPDWORD pcbLengthNeeded); +bool FileStream_GetBitmap(TFileStream * pStream, void * pvBitmap, DWORD cbBitmap, DWORD * pcbLengthNeeded); bool FileStream_Read(TFileStream * pStream, ULONGLONG * pByteOffset, void * pvBuffer, DWORD dwBytesToRead); bool FileStream_Write(TFileStream * pStream, ULONGLONG * pByteOffset, const void * pvBuffer, DWORD dwBytesToWrite); bool FileStream_SetSize(TFileStream * pStream, ULONGLONG NewFileSize); @@ -970,151 +975,144 @@ void FileStream_Close(TFileStream * pStream); // Functions prototypes for Storm.dll // Typedefs for functions exported by Storm.dll -typedef LCID (STORMAPI * SFILESETLOCALE)(LCID); -typedef bool (STORMAPI * SFILEOPENARCHIVE)(const char *, DWORD, DWORD, HANDLE *); -typedef bool (STORMAPI * SFILECLOSEARCHIVE)(HANDLE); -typedef bool (STORMAPI * SFILEOPENFILEEX)(HANDLE, const char *, DWORD, HANDLE *); -typedef bool (STORMAPI * SFILECLOSEFILE)(HANDLE); -typedef DWORD (STORMAPI * SFILEGETFILESIZE)(HANDLE, LPDWORD); -typedef DWORD (STORMAPI * SFILESETFILEPOINTER)(HANDLE, LONG, LONG *, DWORD); -typedef bool (STORMAPI * SFILEREADFILE)(HANDLE, void *, DWORD, LPDWORD, LPOVERLAPPED); +typedef LCID (WINAPI * SFILESETLOCALE)(LCID); +typedef bool (WINAPI * SFILEOPENARCHIVE)(const char *, DWORD, DWORD, HANDLE *); +typedef bool (WINAPI * SFILECLOSEARCHIVE)(HANDLE); +typedef bool (WINAPI * SFILEOPENFILEEX)(HANDLE, const char *, DWORD, HANDLE *); +typedef bool (WINAPI * SFILECLOSEFILE)(HANDLE); +typedef DWORD (WINAPI * SFILEGETFILESIZE)(HANDLE, LPDWORD); +typedef DWORD (WINAPI * SFILESETFILEPOINTER)(HANDLE, LONG, LONG *, DWORD); +typedef bool (WINAPI * SFILEREADFILE)(HANDLE, void *, DWORD, LPDWORD, LPOVERLAPPED); //----------------------------------------------------------------------------- // Functions for manipulation with StormLib global flags -LCID STORMAPI SFileGetLocale(); -LCID STORMAPI SFileSetLocale(LCID lcNewLocale); +// Alternate marker support. This is for MPQs masked as DLLs (*.asi), which +// patch Storm.dll at runtime. Call before SFileOpenArchive +bool WINAPI SFileSetArchiveMarkers(PSFILE_MARKERS pMarkers); + +// Call before SFileOpenFileEx +LCID WINAPI SFileGetLocale(); +LCID WINAPI SFileSetLocale(LCID lcNewLocale); //----------------------------------------------------------------------------- // Functions for archive manipulation -bool STORMAPI SFileOpenArchive(const TCHAR * szMpqName, DWORD dwPriority, DWORD dwFlags, HANDLE * phMpq); -bool STORMAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwCreateFlags, DWORD dwMaxFileCount, HANDLE * phMpq); -bool STORMAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCreateInfo, HANDLE * phMpq); +bool WINAPI SFileOpenArchive(const TCHAR * szMpqName, DWORD dwPriority, DWORD dwFlags, HANDLE * phMpq); +bool WINAPI SFileCreateArchive(const TCHAR * szMpqName, DWORD dwCreateFlags, DWORD dwMaxFileCount, HANDLE * phMpq); +bool WINAPI SFileCreateArchive2(const TCHAR * szMpqName, PSFILE_CREATE_MPQ pCreateInfo, HANDLE * phMpq); -bool STORMAPI SFileSetDownloadCallback(HANDLE hMpq, SFILE_DOWNLOAD_CALLBACK DownloadCB, void * pvUserData); -bool STORMAPI SFileFlushArchive(HANDLE hMpq); -bool STORMAPI SFileCloseArchive(HANDLE hMpq); +bool WINAPI SFileSetDownloadCallback(HANDLE hMpq, SFILE_DOWNLOAD_CALLBACK DownloadCB, void * pvUserData); +bool WINAPI SFileFlushArchive(HANDLE hMpq); +bool WINAPI SFileCloseArchive(HANDLE hMpq); // Adds another listfile into MPQ. The currently added listfile(s) remain, // so you can use this API to combining more listfiles. // Note that this function is internally called by SFileFindFirstFile -int STORMAPI SFileAddListFile(HANDLE hMpq, const TCHAR * szListFile); +int WINAPI SFileAddListFile(HANDLE hMpq, const TCHAR * szListFile); // Archive compacting -bool STORMAPI SFileSetCompactCallback(HANDLE hMpq, SFILE_COMPACT_CALLBACK CompactCB, void * pvUserData); -bool STORMAPI SFileCompactArchive(HANDLE hMpq, const TCHAR * szListFile, bool bReserved); +bool WINAPI SFileSetCompactCallback(HANDLE hMpq, SFILE_COMPACT_CALLBACK CompactCB, void * pvUserData); +bool WINAPI SFileCompactArchive(HANDLE hMpq, const TCHAR * szListFile, bool bReserved); // Changing the maximum file count -DWORD STORMAPI SFileGetMaxFileCount(HANDLE hMpq); -bool STORMAPI SFileSetMaxFileCount(HANDLE hMpq, DWORD dwMaxFileCount); +DWORD WINAPI SFileGetMaxFileCount(HANDLE hMpq); +bool WINAPI SFileSetMaxFileCount(HANDLE hMpq, DWORD dwMaxFileCount); // Changing (attributes) file -DWORD STORMAPI SFileGetAttributes(HANDLE hMpq); -bool STORMAPI SFileSetAttributes(HANDLE hMpq, DWORD dwFlags); -bool STORMAPI SFileUpdateFileAttributes(HANDLE hMpq, const char * szFileName); +DWORD WINAPI SFileGetAttributes(HANDLE hMpq); +bool WINAPI SFileSetAttributes(HANDLE hMpq, DWORD dwFlags); +bool WINAPI SFileUpdateFileAttributes(HANDLE hMpq, const char * szFileName); //----------------------------------------------------------------------------- // Functions for manipulation with patch archives -bool STORMAPI SFileOpenPatchArchive(HANDLE hMpq, const TCHAR * szPatchMpqName, const char * szPatchPathPrefix, DWORD dwFlags); -bool STORMAPI SFileIsPatchedArchive(HANDLE hMpq); +bool WINAPI SFileOpenPatchArchive(HANDLE hMpq, const TCHAR * szPatchMpqName, const char * szPatchPathPrefix, DWORD dwFlags); +bool WINAPI SFileIsPatchedArchive(HANDLE hMpq); //----------------------------------------------------------------------------- // Functions for file manipulation // Reading from MPQ file -bool STORMAPI SFileHasFile(HANDLE hMpq, const char * szFileName); -bool STORMAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * phFile); -DWORD STORMAPI SFileGetFileSize(HANDLE hFile, LPDWORD pdwFileSizeHigh); -DWORD STORMAPI SFileSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod); -bool STORMAPI SFileReadFile(HANDLE hFile, void * lpBuffer, DWORD dwToRead, LPDWORD pdwRead, LPOVERLAPPED lpOverlapped); -bool STORMAPI SFileCloseFile(HANDLE hFile); +bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName); +bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * phFile); +DWORD WINAPI SFileGetFileSize(HANDLE hFile, LPDWORD pdwFileSizeHigh); +DWORD WINAPI SFileSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod); +bool WINAPI SFileReadFile(HANDLE hFile, void * lpBuffer, DWORD dwToRead, LPDWORD pdwRead, LPOVERLAPPED lpOverlapped); +bool WINAPI SFileCloseFile(HANDLE hFile); // Retrieving info about a file in the archive -bool STORMAPI SFileGetFileInfo(HANDLE hMpqOrFile, SFileInfoClass InfoClass, void * pvFileInfo, DWORD cbFileInfo, LPDWORD pcbLengthNeeded); -bool STORMAPI SFileGetFileName(HANDLE hFile, char * szFileName); -bool STORMAPI SFileFreeFileInfo(void * pvFileInfo, SFileInfoClass InfoClass); +bool WINAPI SFileGetFileInfo(HANDLE hMpqOrFile, SFileInfoClass InfoClass, void * pvFileInfo, DWORD cbFileInfo, LPDWORD pcbLengthNeeded); +bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName); +bool WINAPI SFileFreeFileInfo(void * pvFileInfo, SFileInfoClass InfoClass); // High-level extract function -bool STORMAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const TCHAR * szExtracted, DWORD dwSearchScope); +bool WINAPI SFileExtractFile(HANDLE hMpq, const char * szToExtract, const TCHAR * szExtracted, DWORD dwSearchScope); //----------------------------------------------------------------------------- // Functions for file and archive verification // Generates file CRC32 -bool STORMAPI SFileGetFileChecksums(HANDLE hMpq, const char * szFileName, LPDWORD pdwCrc32, char * pMD5); +bool WINAPI SFileGetFileChecksums(HANDLE hMpq, const char * szFileName, LPDWORD pdwCrc32, char * pMD5); // Verifies file against its checksums stored in (attributes) attributes (depending on dwFlags). // For dwFlags, use one or more of MPQ_ATTRIBUTE_MD5 -DWORD STORMAPI SFileVerifyFile(HANDLE hMpq, const char * szFileName, DWORD dwFlags); +DWORD WINAPI SFileVerifyFile(HANDLE hMpq, const char * szFileName, DWORD dwFlags); // Verifies raw data of the archive. Only works for MPQs version 4 or newer -int STORMAPI SFileVerifyRawData(HANDLE hMpq, DWORD dwWhatToVerify, const char * szFileName); +int WINAPI SFileVerifyRawData(HANDLE hMpq, DWORD dwWhatToVerify, const char * szFileName); // Verifies the signature, if present -bool STORMAPI SFileSignArchive(HANDLE hMpq, DWORD dwSignatureType); -DWORD STORMAPI SFileVerifyArchive(HANDLE hMpq); +bool WINAPI SFileSignArchive(HANDLE hMpq, DWORD dwSignatureType); +DWORD WINAPI SFileVerifyArchive(HANDLE hMpq); //----------------------------------------------------------------------------- // Functions for file searching -HANDLE STORMAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DATA * lpFindFileData, const TCHAR * szListFile); -bool STORMAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData); -bool STORMAPI SFileFindClose(HANDLE hFind); +HANDLE WINAPI SFileFindFirstFile(HANDLE hMpq, const char * szMask, SFILE_FIND_DATA * lpFindFileData, const TCHAR * szListFile); +bool WINAPI SFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData); +bool WINAPI SFileFindClose(HANDLE hFind); -HANDLE STORMAPI SListFileFindFirstFile(HANDLE hMpq, const TCHAR * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData); -bool STORMAPI SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData); -bool STORMAPI SListFileFindClose(HANDLE hFind); +HANDLE WINAPI SListFileFindFirstFile(HANDLE hMpq, const TCHAR * szListFile, const char * szMask, SFILE_FIND_DATA * lpFindFileData); +bool WINAPI SListFileFindNextFile(HANDLE hFind, SFILE_FIND_DATA * lpFindFileData); +bool WINAPI SListFileFindClose(HANDLE hFind); // Locale support -int STORMAPI SFileEnumLocales(HANDLE hMpq, const char * szFileName, LCID * plcLocales, LPDWORD pdwMaxLocales, DWORD dwSearchScope); +int WINAPI SFileEnumLocales(HANDLE hMpq, const char * szFileName, LCID * plcLocales, LPDWORD pdwMaxLocales, DWORD dwSearchScope); //----------------------------------------------------------------------------- // Support for adding files to the MPQ -bool STORMAPI SFileCreateFile(HANDLE hMpq, const char * szArchivedName, ULONGLONG FileTime, DWORD dwFileSize, LCID lcLocale, DWORD dwFlags, HANDLE * phFile); -bool STORMAPI SFileWriteFile(HANDLE hFile, const void * pvData, DWORD dwSize, DWORD dwCompression); -bool STORMAPI SFileFinishFile(HANDLE hFile); +bool WINAPI SFileCreateFile(HANDLE hMpq, const char * szArchivedName, ULONGLONG FileTime, DWORD dwFileSize, LCID lcLocale, DWORD dwFlags, HANDLE * phFile); +bool WINAPI SFileWriteFile(HANDLE hFile, const void * pvData, DWORD dwSize, DWORD dwCompression); +bool WINAPI SFileFinishFile(HANDLE hFile); -bool STORMAPI SFileAddFileEx(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwCompression, DWORD dwCompressionNext = MPQ_COMPRESSION_NEXT_SAME); -bool STORMAPI SFileAddFile(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags); -bool STORMAPI SFileAddWave(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwQuality); -bool STORMAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope); -bool STORMAPI SFileRenameFile(HANDLE hMpq, const char * szOldFileName, const char * szNewFileName); -bool STORMAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale); -bool STORMAPI SFileSetDataCompression(DWORD DataCompression); +bool WINAPI SFileAddFileEx(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwCompression, DWORD dwCompressionNext = MPQ_COMPRESSION_NEXT_SAME); +bool WINAPI SFileAddFile(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags); +bool WINAPI SFileAddWave(HANDLE hMpq, const TCHAR * szFileName, const char * szArchivedName, DWORD dwFlags, DWORD dwQuality); +bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope); +bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szOldFileName, const char * szNewFileName); +bool WINAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale); +bool WINAPI SFileSetDataCompression(DWORD DataCompression); -bool STORMAPI SFileSetAddFileCallback(HANDLE hMpq, SFILE_ADDFILE_CALLBACK AddFileCB, void * pvUserData); +bool WINAPI SFileSetAddFileCallback(HANDLE hMpq, SFILE_ADDFILE_CALLBACK AddFileCB, void * pvUserData); //----------------------------------------------------------------------------- // Compression and decompression -int STORMAPI SCompImplode (void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); -int STORMAPI SCompExplode (void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); -int STORMAPI SCompCompress (void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, unsigned uCompressionMask, int nCmpType, int nCmpLevel); -int STORMAPI SCompDecompress (void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); -int STORMAPI SCompDecompress2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); +int WINAPI SCompImplode (void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); +int WINAPI SCompExplode (void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); +int WINAPI SCompCompress (void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer, unsigned uCompressionMask, int nCmpType, int nCmpLevel); +int WINAPI SCompDecompress (void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); +int WINAPI SCompDecompress2(void * pvOutBuffer, int * pcbOutBuffer, void * pvInBuffer, int cbInBuffer); //----------------------------------------------------------------------------- // Non-Windows support for SetLastError/GetLastError -#ifdef FULL -#ifndef PLATFORM_WINDOWS - -void SetLastError(DWORD err); -DWORD GetLastError(); - -#endif -#else - -extern DWORD nLastError; - -DWORD STORMAPI SErrGetLastError(); -void STORMAPI SErrSetLastError(DWORD dwErrCode); +#ifndef STORMLIB_WINDOWS -#define SetLastError SErrSetLastError -#define GetLastError SErrGetLastError +void SetLastError(DWORD dwErrCode); +DWORD GetLastError(); #endif diff --git a/3rdParty/StormLib/src/StormPort.h b/3rdParty/StormLib/src/StormPort.h index 9bd78dfde..bd7947f94 100644 --- a/3rdParty/StormLib/src/StormPort.h +++ b/3rdParty/StormLib/src/StormPort.h @@ -1,438 +1,461 @@ -/*****************************************************************************/ -/* StormPort.h Copyright (c) Marko Friedemann 2001 */ -/*---------------------------------------------------------------------------*/ -/* Portability module for the StormLib library. Contains a wrapper symbols */ -/* to make the compilation under Linux work */ -/* */ -/* Author: Marko Friedemann */ -/* Created at: Mon Jan 29 18:26:01 CEST 2001 */ -/* Computer: whiplash.flachland-chemnitz.de */ -/* System: Linux 2.4.0 on i686 */ -/* */ -/* Author: Sam Wilkins */ -/* System: Mac OS X and port to big endian processor */ -/* */ -/*---------------------------------------------------------------------------*/ -/* Date Ver Who Comment */ -/* -------- ---- --- ------- */ -/* 29.01.01 1.00 Mar Created */ -/* 24.03.03 1.01 Lad Some cosmetic changes */ -/* 12.11.03 1.02 Dan Macintosh compatibility */ -/* 24.07.04 1.03 Sam Mac OS X compatibility */ -/* 22.11.06 1.04 Sam Mac OS X compatibility (for StormLib 6.0) */ -/* 31.12.06 1.05 XPinguin Full GNU/Linux compatibility */ -/* 17.10.12 1.05 Lad Moved error codes so they don't overlap with errno.h */ -/*****************************************************************************/ - -#ifndef __STORMPORT_H__ -#define __STORMPORT_H__ - -#define STORMAPI - -#ifndef __cplusplus - #define bool char - #define true 1 - #define false 0 -#endif - -//----------------------------------------------------------------------------- -// Defines for Windows - -#if !defined(PLATFORM_DEFINED) && defined(_WIN32) - - // In MSVC 8.0, there are some functions declared as deprecated. - #if _MSC_VER >= 1400 - #define _CRT_SECURE_NO_DEPRECATE - #define _CRT_NON_CONFORMING_SWPRINTFS - #endif - - #include - #include - #include - #include - - // Suppress definitions of `min` and `max` macros by : - #define NOMINMAX 1 - #include - - #include - #define PLATFORM_LITTLE_ENDIAN - - #ifdef _WIN64 - #define PLATFORM_64BIT - #else - #define PLATFORM_32BIT - #endif - - #define PLATFORM_WINDOWS - #define PLATFORM_DEFINED // The platform is known now - -#endif - -//----------------------------------------------------------------------------- -// Defines for Mac - -#if !defined(PLATFORM_DEFINED) && defined(__APPLE__) // Mac BSD API - - // Macintosh - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - // Support for PowerPC on Mac OS X - #if (__ppc__ == 1) || (__POWERPC__ == 1) || (_ARCH_PPC == 1) - #include - #include - #endif - - #define PKEXPORT - #define __SYS_ZLIB - #define __SYS_BZLIB - - #ifndef __BIG_ENDIAN__ - #define PLATFORM_LITTLE_ENDIAN - #endif - - #define PLATFORM_MAC - #define PLATFORM_DEFINED // The platform is known now - -#endif - -#if !defined(PLATFORM_DEFINED) && defined(__HAIKU__) - - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #ifndef __BIG_ENDIAN__ - #define PLATFORM_LITTLE_ENDIAN - #endif - - #define PLATFORM_HAIKU - #define PLATFORM_DEFINED // The platform is known now - -#endif - -#if !defined(PLATFORM_DEFINED) && defined(__AMIGA__) - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define PLATFORM_AMIGA -#define PLATFORM_DEFINED - -#endif - -#if !defined(PLATFORM_DEFINED) && defined(__SWITCH__) - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #ifndef __BIG_ENDIAN__ - #define PLATFORM_LITTLE_ENDIAN - #endif - - #define PLATFORM_SWITCH - #define PLATFORM_DEFINED - -#endif - -#if !defined(PLATFORM_DEFINED) && defined(__3DS__) - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #define PLATFORM_LITTLE_ENDIAN - - #define PLATFORM_CTR - #define PLATFORM_DEFINED - -#endif - -#if !defined(PLATFORM_DEFINED) && defined(__vita__) - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #ifndef __BIG_ENDIAN__ - #define PLATFORM_LITTLE_ENDIAN - #endif - - #define PLATFORM_VITA - #define PLATFORM_DEFINED - -#endif - -//----------------------------------------------------------------------------- -// Assumption: we are not on Windows nor Macintosh, so this must be linux *grin* - -#if !defined(PLATFORM_DEFINED) - - #include - #include - #ifndef __vita__ - #include +/*****************************************************************************/ +/* StormPort.h Copyright (c) Marko Friedemann 2001 */ +/*---------------------------------------------------------------------------*/ +/* Portability module for the StormLib library. Contains a wrapper symbols */ +/* to make the compilation under Linux work */ +/* */ +/* Author: Marko Friedemann */ +/* Created at: Mon Jan 29 18:26:01 CEST 2001 */ +/* Computer: whiplash.flachland-chemnitz.de */ +/* System: Linux 2.4.0 on i686 */ +/* */ +/* Author: Sam Wilkins */ +/* System: Mac OS X and port to big endian processor */ +/* */ +/*---------------------------------------------------------------------------*/ +/* Date Ver Who Comment */ +/* -------- ---- --- ------- */ +/* 29.01.01 1.00 Mar Created */ +/* 24.03.03 1.01 Lad Some cosmetic changes */ +/* 12.11.03 1.02 Dan Macintosh compatibility */ +/* 24.07.04 1.03 Sam Mac OS X compatibility */ +/* 22.11.06 1.04 Sam Mac OS X compatibility (for StormLib 6.0) */ +/* 31.12.06 1.05 XPinguin Full GNU/Linux compatibility */ +/* 17.10.12 1.05 Lad Moved error codes so they don't overlap with errno.h */ +/*****************************************************************************/ + +#ifndef __STORMPORT_H__ +#define __STORMPORT_H__ + +#ifndef __cplusplus + #define bool char + #define true 1 + #define false 0 +#endif + +//----------------------------------------------------------------------------- +// Defines for Windows + +#if !defined(STORMLIB_PLATFORM_DEFINED) && defined(_WIN32) + + // In MSVC 8.0, there are some functions declared as deprecated. + #if _MSC_VER >= 1400 + #define _CRT_SECURE_NO_DEPRECATE + #define _CRT_NON_CONFORMING_SWPRINTFS #endif - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - #include - - #ifndef __BIG_ENDIAN__ - #define PLATFORM_LITTLE_ENDIAN - #endif - - #define PLATFORM_LINUX - #define PLATFORM_DEFINED - -#endif - -//----------------------------------------------------------------------------- -// Definition of Windows-specific types for non-Windows platforms - -#ifndef PLATFORM_WINDOWS - #if __LP64__ - #define PLATFORM_64BIT - #else - #define PLATFORM_32BIT - #endif - - // Typedefs for ANSI C - typedef unsigned char BYTE; - typedef int LONG; - typedef unsigned short USHORT; - typedef unsigned int DWORD; - typedef unsigned long DWORD_PTR; - typedef long LONG_PTR; - typedef long INT_PTR; - typedef long long LONGLONG; - typedef unsigned long long ULONGLONG; - typedef void * HANDLE; - typedef void * LPOVERLAPPED; // Unsupported on Linux and Mac - typedef char TCHAR; - typedef unsigned int LCID; - typedef LONG * PLONG; - typedef DWORD * LPDWORD; - typedef BYTE * LPBYTE; - typedef const char * LPCTSTR; - typedef const char * LPCSTR; - typedef char * LPTSTR; - typedef char * LPSTR; - - #ifdef PLATFORM_32BIT - #define _LZMA_UINT32_IS_ULONG - #endif - - // Some Windows-specific defines - #ifndef MAX_PATH - #define MAX_PATH 1024 - #endif - - #ifndef _countof - #define _countof(x) (sizeof(x) / sizeof(x[0])) - #endif - - #define WINAPI __attribute__((stdcall)) - - #define FILE_BEGIN SEEK_SET - #define FILE_CURRENT SEEK_CUR - #define FILE_END SEEK_END - - #define _T(x) x - #define _tcslen strlen - #define _tcscpy strcpy - #define _tcscat strcat - #define _tcschr strchr - #define _tcsrchr strrchr - #define _tcsstr strstr - #define _tcsnicmp strncasecmp - #define _tprintf printf - #define _stprintf sprintf - #define _tremove remove - #define _tmain main - - #define _stricmp strcasecmp - #define _strnicmp strncasecmp - #define _tcsicmp strcasecmp - #define _tcsnicmp strncasecmp - -#endif // !PLATFORM_WINDOWS - -// 64-bit calls are supplied by "normal" calls on Mac -#if defined(PLATFORM_MAC) || defined(PLATFORM_HAIKU) || defined(PLATFORM_AMIGA) || defined(PLATFORM_SWITCH) || defined(PLATFORM_CTR) || defined(PLATFORM_VITA) - #define stat64 stat - #define fstat64 fstat - #define lseek64 lseek - #define ftruncate64 ftruncate - #define off64_t off_t - #define O_LARGEFILE 0 -#endif - -// Platform-specific error codes for UNIX-based platforms -#if defined(PLATFORM_MAC) || defined(PLATFORM_LINUX) || defined(PLATFORM_HAIKU) || defined(PLATFORM_AMIGA) || defined(PLATFORM_SWITCH) || defined(PLATFORM_CTR) || defined(PLATFORM_VITA) - #define ERROR_SUCCESS 0 - #define ERROR_FILE_NOT_FOUND ENOENT - #define ERROR_ACCESS_DENIED EPERM - #define ERROR_INVALID_HANDLE EBADF - #define ERROR_NOT_ENOUGH_MEMORY ENOMEM - #define ERROR_NOT_SUPPORTED ENOTSUP - #define ERROR_INVALID_PARAMETER EINVAL - #define ERROR_NEGATIVE_SEEK EINVAL - #define ERROR_DISK_FULL ENOSPC - #define ERROR_ALREADY_EXISTS EEXIST - #define ERROR_INSUFFICIENT_BUFFER ENOBUFS - #define ERROR_BAD_FORMAT 1000 // No such error code under Linux - #define ERROR_NO_MORE_FILES 1001 // No such error code under Linux - #define ERROR_HANDLE_EOF 1002 // No such error code under Linux - #define ERROR_CAN_NOT_COMPLETE 1003 // No such error code under Linux - #define ERROR_FILE_CORRUPT 1004 // No such error code under Linux -#endif - -//----------------------------------------------------------------------------- -// Swapping functions - -#ifdef PLATFORM_LITTLE_ENDIAN - #define BSWAP_INT16_UNSIGNED(a) (a) - #define BSWAP_INT16_SIGNED(a) (a) - #define BSWAP_INT32_UNSIGNED(a) (a) - #define BSWAP_INT32_SIGNED(a) (a) - #define BSWAP_INT64_SIGNED(a) (a) - #define BSWAP_INT64_UNSIGNED(a) (a) - #define BSWAP_ARRAY16_UNSIGNED(a,b) {} - #define BSWAP_ARRAY32_UNSIGNED(a,b) {} - #define BSWAP_ARRAY64_UNSIGNED(a,b) {} - #define BSWAP_PART_HEADER(a) {} - #define BSWAP_TMPQHEADER(a,b) {} - #define BSWAP_TMPKHEADER(a) {} -#else - -#ifdef __cplusplus - extern "C" { -#endif - int16_t SwapInt16(uint16_t); - uint16_t SwapUInt16(uint16_t); - int32_t SwapInt32(uint32_t); - uint32_t SwapUInt32(uint32_t); - int64_t SwapInt64(uint64_t); - uint64_t SwapUInt64(uint64_t); - void ConvertUInt16Buffer(void * ptr, size_t length); - void ConvertUInt32Buffer(void * ptr, size_t length); - void ConvertUInt64Buffer(void * ptr, size_t length); - void ConvertTMPQUserData(void *userData); - void ConvertTMPQHeader(void *header, uint16_t wPart); - void ConvertTMPKHeader(void *header); -#ifdef __cplusplus - } -#endif - #define BSWAP_INT16_SIGNED(a) SwapInt16((a)) - #define BSWAP_INT16_UNSIGNED(a) SwapUInt16((a)) - #define BSWAP_INT32_SIGNED(a) SwapInt32((a)) - #define BSWAP_INT32_UNSIGNED(a) SwapUInt32((a)) - #define BSWAP_INT64_SIGNED(a) SwapInt64((a)) - #define BSWAP_INT64_UNSIGNED(a) SwapUInt64((a)) - #define BSWAP_ARRAY16_UNSIGNED(a,b) ConvertUInt16Buffer((a),(b)) - #define BSWAP_ARRAY32_UNSIGNED(a,b) ConvertUInt32Buffer((a),(b)) - #define BSWAP_ARRAY64_UNSIGNED(a,b) ConvertUInt64Buffer((a),(b)) - #define BSWAP_TMPQHEADER(a,b) ConvertTMPQHeader((a),(b)) - #define BSWAP_TMPKHEADER(a) ConvertTMPKHeader((a)) - -#endif - -//----------------------------------------------------------------------------- -// Macro for deprecated symbols - -/* -#ifdef _MSC_VER - #if _MSC_FULL_VER >= 140050320 - #define STORMLIB_DEPRECATED(_Text) __declspec(deprecated(_Text)) - #else - #define STORMLIB_DEPRECATED(_Text) __declspec(deprecated) - #endif -#else - #ifdef __GNUC__ - #define STORMLIB_DEPRECATED(_Text) __attribute__((deprecated)) - #else - #define STORMLIB_DEPRECATED(_Text) __attribute__((deprecated(_Text))) - #endif -#endif - -// When a flag is deprecated, use this macro -#ifndef _STORMLIB_NO_DEPRECATE - #define STORMLIB_DEPRECATED_FLAG(type, oldflag, newflag) \ - const STORMLIB_DEPRECATED(#oldflag " is deprecated. Use " #newflag ". To supress this warning, define _STORMLIB_NO_DEPRECATE") static type oldflag = (type)newflag; -#else -#define STORMLIB_DEPRECATED_FLAG(type, oldflag, newflag) static type oldflag = (type)newflag; -#endif -*/ - -// -// MINIWIN changes -// - -#define bool int - -#endif // __STORMPORT_H__ + + #include + #include + #include + #include + + // Suppress definitions of `min` and `max` macros by : + #define NOMINMAX 1 + #include + + #include + #define STORMLIB_LITTLE_ENDIAN + + #ifdef _WIN64 + #define STORMLIB_64BIT + #else + #define STORMLIB_32BIT + #endif + + #define STORMLIB_CDECL __cdecl + + #define STORMLIB_WINDOWS + #define STORMLIB_PLATFORM_DEFINED // The platform is known now + +#endif + +//----------------------------------------------------------------------------- +// Defines for Mac + +#if !defined(STORMLIB_PLATFORM_DEFINED) && defined(__APPLE__) // Mac BSD API + + // Macintosh + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + // Support for PowerPC on Max OS X + #if (__ppc__ == 1) || (__POWERPC__ == 1) || (_ARCH_PPC == 1) + #include + #include + #endif + + #define PKEXPORT + #define __SYS_ZLIB + #define __SYS_BZLIB + + #ifndef __BIG_ENDIAN__ + #define STORMLIB_LITTLE_ENDIAN + #endif + + #define STORMLIB_MAC + #define STORMLIB_HAS_MMAP // Indicate that we have mmap support + #define STORMLIB_PLATFORM_DEFINED // The platform is known now + +#endif + +//----------------------------------------------------------------------------- +// Defines for HAIKU platform + +#if !defined(STORMLIB_PLATFORM_DEFINED) && defined(__HAIKU__) + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #ifndef __BIG_ENDIAN__ + #define STORMLIB_LITTLE_ENDIAN + #endif + + #define STORMLIB_MAC // Use Mac compatible code + #define STORMLIB_HAIKU + #define STORMLIB_PLATFORM_DEFINED // The platform is known now + +#endif + +//----------------------------------------------------------------------------- +// Defines for AMIGA platform + +#if !defined(STORMLIB_PLATFORM_DEFINED) && defined(__AMIGA__) + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #ifndef __BIG_ENDIAN__ + #define STORMLIB_LITTLE_ENDIAN + #endif + + #define STORMLIB_MAC // Use Mac compatible code + #define STORMLIB_AMIGA + #define STORMLIB_PLATFORM_DEFINED + +#endif + +//----------------------------------------------------------------------------- +// Defines for Switch platform + +#if !defined(STORMLIB_PLATFORM_DEFINED) && defined(__SWITCH__) + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #ifndef __BIG_ENDIAN__ + #define STORMLIB_LITTLE_ENDIAN + #endif + + #define STORMLIB_MAC // Use Mac compatible code + #define STORMLIB_SWITCH + #define STORMLIB_PLATFORM_DEFINED + +#endif + +//----------------------------------------------------------------------------- +// Defines for 3DS platform + +#if !defined(STORMLIB_PLATFORM_DEFINED) && defined(__3DS__) + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #define STORMLIB_LITTLE_ENDIAN + + #define STORMLIB_MAC // Use Mac compatible code + #define STORMLIB_CTR + #define STORMLIB_PLATFORM_DEFINED + +#endif + +//----------------------------------------------------------------------------- +// Defines for Vita platform + +#if !defined(STORMLIB_PLATFORM_DEFINED) && defined(__vita__) + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #ifndef __BIG_ENDIAN__ + #define STORMLIB_LITTLE_ENDIAN + #endif + + #define STORMLIB_MAC // Use Mac compatible code + #define STORMLIB_VITA + #define STORMLIB_PLATFORM_DEFINED + +#endif + +//----------------------------------------------------------------------------- +// Assumption: If the platform is not defined, this must be Linux *grin* + +#if !defined(STORMLIB_PLATFORM_DEFINED) + + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + #include + + #ifndef __BIG_ENDIAN__ + #define STORMLIB_LITTLE_ENDIAN + #endif + + #define STORMLIB_LINUX + #define STORMLIB_HAS_MMAP // Indicate that we have mmap support + #define STORMLIB_PLATFORM_DEFINED + +#endif + +//----------------------------------------------------------------------------- +// Definition of Windows-specific types for non-Windows platforms + +#ifndef STORMLIB_WINDOWS + #if __LP64__ + #define STORMLIB_64BIT + #else + #define STORMLIB_32BIT + #endif + + // __cdecl meand nothing on non-Windows + #define STORMLIB_CDECL /* */ + + // Typedefs for ANSI C + typedef unsigned char BYTE; + typedef unsigned short USHORT; + typedef int LONG; + typedef unsigned int DWORD; + typedef unsigned long DWORD_PTR; + typedef long LONG_PTR; + typedef long INT_PTR; + typedef long long LONGLONG; + typedef unsigned long long ULONGLONG; + typedef void * HANDLE; + typedef void * LPOVERLAPPED; // Unsupported on Linux and Mac + typedef char TCHAR; + typedef unsigned int LCID; + typedef LONG * PLONG; + typedef DWORD * LPDWORD; + typedef BYTE * LPBYTE; + typedef const char * LPCTSTR; + typedef const char * LPCSTR; + typedef char * LPTSTR; + typedef char * LPSTR; + + #ifdef STORMLIB_32BIT + #define _LZMA_UINT32_IS_ULONG + #endif + + // Some Windows-specific defines + #ifndef MAX_PATH + #define MAX_PATH 1024 + #endif + + #define WINAPI + + #define FILE_BEGIN SEEK_SET + #define FILE_CURRENT SEEK_CUR + #define FILE_END SEEK_END + + #define _T(x) x + #define _tcslen strlen + #define _tcscpy strcpy + #define _tcscat strcat + #define _tcschr strchr + #define _tcsrchr strrchr + #define _tcsstr strstr + #define _tcsnicmp strncasecmp + #define _tprintf printf + #define _stprintf sprintf + #define _tremove remove + #define _tmain main + + #define _stricmp strcasecmp + #define _strnicmp strncasecmp + #define _tcsicmp strcasecmp + #define _tcsnicmp strncasecmp + +#endif // !STORMLIB_WINDOWS + +// 64-bit calls are supplied by "normal" calls on Mac +#if defined(STORMLIB_MAC) + #define stat64 stat + #define fstat64 fstat + #define lseek64 lseek + #define ftruncate64 ftruncate + #define off64_t off_t + #define O_LARGEFILE 0 +#endif + +// Platform-specific error codes for UNIX-based platforms +#if defined(STORMLIB_MAC) || defined(STORMLIB_LINUX) + #define ERROR_SUCCESS 0 + #define ERROR_FILE_NOT_FOUND ENOENT + #define ERROR_ACCESS_DENIED EPERM + #define ERROR_INVALID_HANDLE EBADF + #define ERROR_NOT_ENOUGH_MEMORY ENOMEM + #define ERROR_NOT_SUPPORTED ENOTSUP + #define ERROR_INVALID_PARAMETER EINVAL + #define ERROR_NEGATIVE_SEEK ESPIPE + #define ERROR_DISK_FULL ENOSPC + #define ERROR_ALREADY_EXISTS EEXIST + #define ERROR_INSUFFICIENT_BUFFER ENOBUFS + #define ERROR_BAD_FORMAT 1000 // No such error code under Linux + #define ERROR_NO_MORE_FILES 1001 // No such error code under Linux + #define ERROR_HANDLE_EOF 1002 // No such error code under Linux + #define ERROR_CAN_NOT_COMPLETE 1003 // No such error code under Linux + #define ERROR_FILE_CORRUPT 1004 // No such error code under Linux +#endif + +#ifndef _countof + #define _countof(x) (sizeof(x) / sizeof(x[0])) +#endif + +//----------------------------------------------------------------------------- +// Swapping functions + +#ifdef STORMLIB_LITTLE_ENDIAN + #define BSWAP_INT16_UNSIGNED(a) (a) + #define BSWAP_INT16_SIGNED(a) (a) + #define BSWAP_INT32_UNSIGNED(a) (a) + #define BSWAP_INT32_SIGNED(a) (a) + #define BSWAP_INT64_SIGNED(a) (a) + #define BSWAP_INT64_UNSIGNED(a) (a) + #define BSWAP_ARRAY16_UNSIGNED(a,b) {} + #define BSWAP_ARRAY32_UNSIGNED(a,b) {} + #define BSWAP_ARRAY64_UNSIGNED(a,b) {} + #define BSWAP_PART_HEADER(a) {} + #define BSWAP_TMPQHEADER(a,b) {} + #define BSWAP_TMPKHEADER(a) {} +#else + +#ifdef __cplusplus + extern "C" { +#endif + int16_t SwapInt16(uint16_t); + uint16_t SwapUInt16(uint16_t); + int32_t SwapInt32(uint32_t); + uint32_t SwapUInt32(uint32_t); + int64_t SwapInt64(uint64_t); + uint64_t SwapUInt64(uint64_t); + void ConvertUInt16Buffer(void * ptr, size_t length); + void ConvertUInt32Buffer(void * ptr, size_t length); + void ConvertUInt64Buffer(void * ptr, size_t length); + void ConvertTMPQUserData(void *userData); + void ConvertTMPQHeader(void *header, uint16_t wPart); + void ConvertTMPKHeader(void *header); +#ifdef __cplusplus + } +#endif + #define BSWAP_INT16_SIGNED(a) SwapInt16((a)) + #define BSWAP_INT16_UNSIGNED(a) SwapUInt16((a)) + #define BSWAP_INT32_SIGNED(a) SwapInt32((a)) + #define BSWAP_INT32_UNSIGNED(a) SwapUInt32((a)) + #define BSWAP_INT64_SIGNED(a) SwapInt64((a)) + #define BSWAP_INT64_UNSIGNED(a) SwapUInt64((a)) + #define BSWAP_ARRAY16_UNSIGNED(a,b) ConvertUInt16Buffer((a),(b)) + #define BSWAP_ARRAY32_UNSIGNED(a,b) ConvertUInt32Buffer((a),(b)) + #define BSWAP_ARRAY64_UNSIGNED(a,b) ConvertUInt64Buffer((a),(b)) + #define BSWAP_TMPQHEADER(a,b) ConvertTMPQHeader((a),(b)) + #define BSWAP_TMPKHEADER(a) ConvertTMPKHeader((a)) +#endif + +//----------------------------------------------------------------------------- +// Macro for deprecated symbols + +/* +#ifdef _MSC_VER + #if _MSC_FULL_VER >= 140050320 + #define STORMLIB_DEPRECATED(_Text) __declspec(deprecated(_Text)) + #else + #define STORMLIB_DEPRECATED(_Text) __declspec(deprecated) + #endif +#else + #ifdef __GNUC__ + #define STORMLIB_DEPRECATED(_Text) __attribute__((deprecated)) + #else + #define STORMLIB_DEPRECATED(_Text) __attribute__((deprecated(_Text))) + #endif +#endif + +// When a flag is deprecated, use this macro +#ifndef _STORMLIB_NO_DEPRECATE + #define STORMLIB_DEPRECATED_FLAG(type, oldflag, newflag) \ + const STORMLIB_DEPRECATED(#oldflag " is deprecated. Use " #newflag ". To supress this warning, define _STORMLIB_NO_DEPRECATE") static type oldflag = (type)newflag; +#else +#define STORMLIB_DEPRECATED_FLAG(type, oldflag, newflag) static type oldflag = (type)newflag; +#endif +*/ + +#endif // __STORMPORT_H__