diff options
Diffstat (limited to 'dep/CascLib/src')
23 files changed, 317 insertions, 207 deletions
diff --git a/dep/CascLib/src/CascCommon.h b/dep/CascLib/src/CascCommon.h index 19a6aa98444..b47674f1544 100644 --- a/dep/CascLib/src/CascCommon.h +++ b/dep/CascLib/src/CascCommon.h @@ -21,6 +21,10 @@ #include <zlib.h> #endif +#if defined(_DEBUG) && !defined(CASCLIB_NODEBUG) +#define CASCLIB_DEBUG +#endif + #include "CascPort.h" #include "common/Common.h" #include "common/Array.h" @@ -48,7 +52,7 @@ //----------------------------------------------------------------------------- // CascLib private defines -#ifdef _DEBUG +#if defined(CASCLIB_DEBUG) && defined(CASCLIB_DEV) #define BREAK_ON_XKEY3(CKey, v0, v1, v2) if(CKey[0] == v0 && CKey[1] == v1 && CKey[2] == v2) { __debugbreak(); } #define BREAKIF(condition) if(condition) { __debugbreak(); } #else @@ -60,7 +64,7 @@ #define CASC_MAGIC_FILE 0x454C494643534143 // 'CASCFILE' #define CASC_MAGIC_FIND 0x444E494643534143 // 'CASCFIND' -// The maximum size of an inline file +// The maximum size of an online file #define CASC_MAX_ONLINE_FILE_SIZE 0x40000000 //----------------------------------------------------------------------------- @@ -456,7 +460,7 @@ DWORD GetFileSpanInfo(PCASC_CKEY_ENTRY pCKeyEntry, PULONGLONG PtrContentSize, PU DWORD FetchCascFile(TCascStorage * hs, CPATH_TYPE PathType, LPBYTE pbEKey, LPCTSTR szExtension, CASC_PATH<TCHAR> & LocalPath, PCASC_ARCHIVE_INFO pArchiveInfo = NULL); DWORD CheckCascBuildFileExact(CASC_BUILD_FILE & BuildFile, LPCTSTR szLocalPath); DWORD CheckCascBuildFileDirs(CASC_BUILD_FILE & BuildFile, LPCTSTR szLocalPath); -DWORD CheckOnlineStorage(PCASC_OPEN_STORAGE_ARGS pArgs, CASC_BUILD_FILE & BuildFile, DWORD dwFeatures); +DWORD CheckOnlineStorage(PCASC_OPEN_STORAGE_ARGS pArgs, CASC_BUILD_FILE & BuildFile, bool bOnlineStorage); DWORD CheckArchiveFilesDirectories(TCascStorage * hs); DWORD CheckDataFilesDirectory(TCascStorage * hs); DWORD LoadMainFile(TCascStorage * hs); @@ -506,7 +510,7 @@ DWORD RootHandler_CreateInstall(TCascStorage * hs, CASC_BLOB & InstallFile); //----------------------------------------------------------------------------- // Dumpers (CascDumpData.cpp) -#ifdef _DEBUG +#ifdef CASCLIB_DEBUG void CascDumpData(LPCSTR szFileName, const void * pvData, size_t cbData); void CascDumpFile(HANDLE hFile, const char * szDumpFile = NULL); void CascDumpStorage(HANDLE hStorage, const char * szDumpFile = NULL); diff --git a/dep/CascLib/src/CascDecrypt.cpp b/dep/CascLib/src/CascDecrypt.cpp index 2eba7a5ebff..0492a50a7c1 100644 --- a/dep/CascLib/src/CascDecrypt.cpp +++ b/dep/CascLib/src/CascDecrypt.cpp @@ -51,8 +51,8 @@ static CASC_ENCRYPTION_KEY StaticCascKeys[] = // Battle.net app { 0x2C547F26A2613E01ULL, { 0x37, 0xC5, 0x0C, 0x10, 0x2D, 0x4C, 0x9E, 0x3A, 0x5A, 0xC0, 0x69, 0xF0, 0x72, 0xB1, 0x41, 0x7D } }, // Battle.net App Alpha 1.5.0 - // Starcraft -// { 0xD0CAE11366CEEA83ULL, { 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x??, 0x?? } }, // 1.12.3.2609 (build 45364) + // StarCraft II + { 0xD0CAE11366CEEA83ULL, { 0x00, 0x41, 0x61, 0x07, 0x8E, 0x5A, 0x61, 0x20, 0x32, 0x1E, 0xA5, 0xFF, 0xE4, 0xDC, 0xD1, 0x26 } }, // Nova Covert Ops Expansion // Warcraft III Reforged beta (build) { 0x6E4296823E7D561EULL, { 0xC0, 0xBF, 0xA2, 0x94, 0x3A, 0xC3, 0xE9, 0x22, 0x86, 0xE4, 0x44, 0x3E, 0xE3, 0x56, 0x0D, 0x65 } }, // 1.32.0.13369 Base content (Beta Week 0) diff --git a/dep/CascLib/src/CascDumpData.cpp b/dep/CascLib/src/CascDumpData.cpp index e29e93d093c..1c26efbeba1 100644 --- a/dep/CascLib/src/CascDumpData.cpp +++ b/dep/CascLib/src/CascDumpData.cpp @@ -12,7 +12,7 @@ #include "CascLib.h" #include "CascCommon.h" -#ifdef _DEBUG // The entire feature is only valid for debug purposes +#ifdef CASCLIB_DEBUG // The entire feature is only valid for debug purposes //----------------------------------------------------------------------------- // Forward definitions @@ -78,7 +78,7 @@ static void DumpKey(FILE * fp, const char * szInFormat, LPBYTE pbData, size_t cb // If there will be more lines, then we clear the entire part until "%s" if(szFormatSpec != NULL) memset(szFormat, ' ', (szFormatSpec - szFormat)); - + // Move pointers pbData += MD5_HASH_SIZE; } @@ -541,9 +541,9 @@ void CascDumpStorage(HANDLE hStorage, const char * szDumpFile) } } -#else // _DEBUG +#else // CASCLIB_DEBUG // so linker won't mind this .cpp file is empty in non-DEBUG builds void unused_symbol() { } -#endif // _DEBUG +#endif // CASCLIB_DEBUG diff --git a/dep/CascLib/src/CascFiles.cpp b/dep/CascLib/src/CascFiles.cpp index f2546e1befa..550b558f7da 100644 --- a/dep/CascLib/src/CascFiles.cpp +++ b/dep/CascLib/src/CascFiles.cpp @@ -1410,6 +1410,23 @@ static LPTSTR CheckForDirectory(LPCTSTR szParentFolder, LPCTSTR szSubFolder) return szLocalPath; } +static LPTSTR CheckForDirectories(LPCTSTR szParentFolder, ...) +{ + LPCTSTR szSubDir; + va_list argList; + LPTSTR szFolder = NULL; + + va_start(argList, szParentFolder); + while((szSubDir = va_arg(argList, LPCTSTR)) != NULL) + { + if((szFolder = CheckForDirectory(szParentFolder, szSubDir)) != NULL) + break; + } + va_end(argList); + + return szFolder; +} + //----------------------------------------------------------------------------- // Public functions @@ -1522,10 +1539,10 @@ DWORD CheckCascBuildFileDirs(CASC_BUILD_FILE & BuildFile, LPCTSTR szLocalPath) return dwErrCode; } -DWORD CheckOnlineStorage(PCASC_OPEN_STORAGE_ARGS pArgs, CASC_BUILD_FILE & BuildFile, DWORD dwFeatures) +DWORD CheckOnlineStorage(PCASC_OPEN_STORAGE_ARGS pArgs, CASC_BUILD_FILE & BuildFile, bool bOnlineStorage) { // If the online storage is required, we try to extract the product code - if((dwFeatures & CASC_FEATURE_ONLINE) && (pArgs->szCodeName != NULL)) + if((bOnlineStorage) && (pArgs->szCodeName != NULL)) { CASC_PATH<TCHAR> FilePath(pArgs->szLocalPath, _T("versions"), NULL); @@ -1554,31 +1571,22 @@ DWORD CheckArchiveFilesDirectories(TCascStorage * hs) { if((szDataPath = CheckForDirectory(hs->szRootPath, DataDirs[i])) != NULL) { - // If we found the data path, we also need to initialize the index path - // Check the config folder if((szConfigPath = CheckForDirectory(szDataPath, _T("config"))) != NULL) { // First, check for more common "data" subdirectory - if((szIndexPath = CheckForDirectory(szDataPath, _T("data"))) == NULL) - { - // Second, try the "darch" subdirectory (older builds of HOTS - Alpha) - szIndexPath = CheckForDirectory(szDataPath, _T("darch")); - } - - if(szIndexPath != NULL) + // Second, try the "darch" subdirectory (older builds of HOTS - Alpha) + if((szIndexPath = CheckForDirectories(szDataPath, _T("data"), _T("darch"), NULL)) != NULL) { hs->szDataPath = szDataPath; hs->szConfigPath = szConfigPath; hs->szIndexPath = szIndexPath; return ERROR_SUCCESS; } + CASC_FREE(szConfigPath); } + CASC_FREE(szDataPath); } - - CASC_FREE(szDataPath); - CASC_FREE(szConfigPath); - CASC_FREE(szIndexPath); } // One of the paths was not found diff --git a/dep/CascLib/src/CascFindFile.cpp b/dep/CascLib/src/CascFindFile.cpp index 76efa12ae43..6b7edbdbba4 100644 --- a/dep/CascLib/src/CascFindFile.cpp +++ b/dep/CascLib/src/CascFindFile.cpp @@ -38,7 +38,7 @@ static void SupplyFakeFileName(PCASC_FIND_DATA pFindData, PCASC_CKEY_ENTRY pCKey // If there is a file data ID, create fake file name if(pFindData->dwFileDataId != CASC_INVALID_ID) { - CascStrPrintf(pFindData->szFileName, _countof(pFindData->szFileName), "FILE%08X.dat", pFindData->dwFileDataId); + CascStrPrintf(pFindData->szFileName, _countof(pFindData->szFileName), CASC_FILEID_FORMAT, pFindData->dwFileDataId); pFindData->NameType = CascNameDataId; return; } diff --git a/dep/CascLib/src/CascLib.h b/dep/CascLib/src/CascLib.h index b63869a49b4..01e9c314fd0 100644 --- a/dep/CascLib/src/CascLib.h +++ b/dep/CascLib/src/CascLib.h @@ -75,6 +75,7 @@ extern "C" { //----------------------------------------------------------------------------- // Defines +// Version information #define CASCLIB_VERSION 0x0300 // CascLib version - integral (3.0) #define CASCLIB_VERSION_STRING "3.0" // CascLib version - string @@ -110,11 +111,18 @@ extern "C" { #define CASC_LOCALE_PTPT 0x00010000 // Content flags on WoW +#define CASC_CFLAG_INSTALL 0x04 #define CASC_CFLAG_LOAD_ON_WINDOWS 0x08 #define CASC_CFLAG_LOAD_ON_MAC 0x10 +#define CASC_CFLAG_X86_32 0x20 +#define CASC_CFLAG_X86_64 0x40 #define CASC_CFLAG_LOW_VIOLENCE 0x80 #define CASC_CFLAG_DONT_LOAD 0x100 +#define CASC_CFLAG_UPDATE_PLUGIN 0x800 +#define CASC_CFLAG_ARM64 0x8000 +#define CASC_CFLAG_ENCRYPTED 0x8000000 #define CASC_CFLAG_NO_NAME_HASH 0x10000000 +#define CASC_CFLAG_UNCMN_RESOLUTION 0x20000000 // Uncommon resolution #define CASC_CFLAG_BUNDLE 0x40000000 #define CASC_CFLAG_NO_COMPRESSION 0x80000000 @@ -153,6 +161,9 @@ extern "C" { // Maximum length of encryption key #define CASC_KEY_LENGTH 0x10 +// Default format string for the file ID +#define CASC_FILEID_FORMAT "FILE%08X.dat" + //----------------------------------------------------------------------------- // Structures @@ -191,7 +202,7 @@ typedef enum _CASC_FILE_INFO_CLASS typedef enum _CASC_NAME_TYPE { CascNameFull, // Fully qualified file name - CascNameDataId, // Name created from file data id (FILE%08X.dat) + CascNameDataId, // Name created from file data id (CASC_FILEID_FORMAT) CascNameCKey, // Name created as string representation of CKey CascNameEKey // Name created as string representation of EKey } CASC_NAME_TYPE, *PCASC_NAME_TYPE; @@ -363,6 +374,7 @@ bool WINAPI CascCloseStorage(HANDLE hStorage); bool WINAPI CascOpenFile(HANDLE hStorage, const void * pvFileName, DWORD dwLocaleFlags, DWORD dwOpenFlags, HANDLE * PtrFileHandle); bool WINAPI CascOpenLocalFile(LPCTSTR szFileName, DWORD dwOpenFlags, HANDLE * PtrFileHandle); bool WINAPI CascGetFileInfo(HANDLE hFile, CASC_FILE_INFO_CLASS InfoClass, void * pvFileInfo, size_t cbFileInfo, size_t * pcbLengthNeeded); +bool WINAPI CascSetFileFlags(HANDLE hFile, DWORD dwOpenFlags); bool WINAPI CascGetFileSize64(HANDLE hFile, PULONGLONG PtrFileSize); bool WINAPI CascSetFilePointer64(HANDLE hFile, LONGLONG DistanceToMove, PULONGLONG PtrNewPos, DWORD dwMoveMethod); bool WINAPI CascReadFile(HANDLE hFile, void * lpBuffer, DWORD dwToRead, PDWORD pdwRead); diff --git a/dep/CascLib/src/CascOpenStorage.cpp b/dep/CascLib/src/CascOpenStorage.cpp index 24afbc73e7b..16f0c28da48 100644 --- a/dep/CascLib/src/CascOpenStorage.cpp +++ b/dep/CascLib/src/CascOpenStorage.cpp @@ -29,7 +29,7 @@ #define CHECKED_KEY {0x00, 0x00, 0x0F, 0x84} -#if defined(_DEBUG) && defined(CHECKED_KEY) +#if defined(CASCLIB_DEBUG) && defined(CHECKED_KEY) inline bool CheckForXKey(LPBYTE XKey) { @@ -117,7 +117,7 @@ TCascStorage::~TCascStorage() // Free the blobs FreeCascBlob(&CdnConfigKey); FreeCascBlob(&CdnBuildKey); - + FreeCascBlob(&ArchiveGroup); FreeCascBlob(&ArchivesKey); FreeCascBlob(&PatchArchivesKey); @@ -423,8 +423,8 @@ static int LoadEncodingCKeyPage(TCascStorage * hs, CASC_ENCODING_HEADER & EnHead if(pFileEntry->EKeyCount == 0) break; - // Example of a file entry with multiple EKeys: - // Overwatch build 24919, CKey: 0e 90 94 fa d2 cb 85 ac d0 7c ea 09 f9 c5 ba 00 + // Example of a file entry with multiple EKeys: + // Overwatch build 24919, CKey: 0e 90 94 fa d2 cb 85 ac d0 7c ea 09 f9 c5 ba 00 // BREAKIF(pFileEntry->EKeyCount > 1); // BREAK_ON_XKEY3(pFileEntry->CKey, 0x34, 0x82, 0x1f); @@ -613,7 +613,7 @@ int CaptureDownloadTag(CASC_DOWNLOAD_HEADER & DlHeader, CASC_TAG_ENTRY1 & DlTag, pbFilePtr++; if(pbFilePtr >= pbFileEnd) return ERROR_BAD_FORMAT; - + // Save the length of the tag name DlTag.NameLength = (pbFilePtr - pbSaveFilePtr); pbFilePtr++; @@ -630,7 +630,7 @@ int CaptureDownloadTag(CASC_DOWNLOAD_HEADER & DlHeader, CASC_TAG_ENTRY1 & DlTag, // Get the bitmap length. // If the bitmap is last in the list and it's shorter than declared, we make it shorter DlTag.BitmapLength = GetTagBitmapLength(pbFilePtr, pbFileEnd, DlHeader.EntryCount); - + // Get the entry length DlTag.TagLength = (pbFilePtr - pbSaveFilePtr) + DlTag.BitmapLength; return ERROR_SUCCESS; @@ -769,7 +769,7 @@ static int LoadDownloadManifest(TCascStorage * hs) if(dwErrCode == ERROR_SUCCESS) { // Parse the entire download manifest - dwErrCode = LoadDownloadManifest(hs, DlHeader, DownloadFile.pbData, DownloadFile.pbData + DownloadFile.cbData); + dwErrCode = LoadDownloadManifest(hs, DlHeader, DownloadFile.pbData, DownloadFile.pbData + DownloadFile.cbData); } } @@ -1122,6 +1122,7 @@ static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs, L // Merge features hs->dwFeatures |= (dwFeatures & (CASC_FEATURE_DATA_ARCHIVES | CASC_FEATURE_DATA_FILES | CASC_FEATURE_ONLINE)); hs->dwFeatures |= (pArgs->dwFlags & CASC_FEATURE_FORCE_DOWNLOAD); + hs->dwFeatures |= (BuildFileType == CascVersions) ? CASC_FEATURE_ONLINE : 0; hs->BuildFileType = BuildFileType; // Copy the name of the build file @@ -1390,24 +1391,23 @@ bool WINAPI CascOpenStorageEx(LPCTSTR szParams, PCASC_OPEN_STORAGE_ARGS pArgs, b if((hs = new TCascStorage()) != NULL) { CASC_BUILD_FILE BuildFile = {NULL}; - DWORD dwFeatures = bOnlineStorage ? CASC_FEATURE_ONLINE : 0; - + // Check for one of the supported main files (.build.info, .build.db, versions) if((dwErrCode = CheckCascBuildFileExact(BuildFile, pArgs->szLocalPath)) == ERROR_SUCCESS) { - dwErrCode = LoadCascStorage(hs, pArgs, BuildFile.szFullPath, BuildFile.BuildFileType, dwFeatures | CASC_FEATURE_DATA_ARCHIVES | CASC_FEATURE_DATA_FILES); + dwErrCode = LoadCascStorage(hs, pArgs, BuildFile.szFullPath, BuildFile.BuildFileType, CASC_FEATURE_DATA_ARCHIVES | CASC_FEATURE_DATA_FILES); } // Search the folder and upper folders for the build file else if((dwErrCode = CheckCascBuildFileDirs(BuildFile, pArgs->szLocalPath)) == ERROR_SUCCESS) { - dwErrCode = LoadCascStorage(hs, pArgs, BuildFile.szFullPath, BuildFile.BuildFileType, dwFeatures | CASC_FEATURE_DATA_ARCHIVES | CASC_FEATURE_DATA_FILES); + dwErrCode = LoadCascStorage(hs, pArgs, BuildFile.szFullPath, BuildFile.BuildFileType, CASC_FEATURE_DATA_ARCHIVES | CASC_FEATURE_DATA_FILES); } // If the caller requested an online storage, we must have the code name - else if((dwErrCode = CheckOnlineStorage(pArgs, BuildFile, dwFeatures)) == ERROR_SUCCESS) + else if((dwErrCode = CheckOnlineStorage(pArgs, BuildFile, bOnlineStorage)) == ERROR_SUCCESS) { - dwErrCode = LoadCascStorage(hs, pArgs, BuildFile.szFullPath, BuildFile.BuildFileType, dwFeatures | CASC_FEATURE_DATA_FILES); + dwErrCode = LoadCascStorage(hs, pArgs, BuildFile.szFullPath, BuildFile.BuildFileType, CASC_FEATURE_DATA_FILES); } } } @@ -1426,7 +1426,7 @@ bool WINAPI CascOpenStorageEx(LPCTSTR szParams, PCASC_OPEN_STORAGE_ARGS pArgs, b } // -// Opens a local CASC storage +// Opens a local CASC storage // // szParams: "local_path:code_name", like "C:\\Games\\World of Warcraft:wowt" // diff --git a/dep/CascLib/src/CascPort.h b/dep/CascLib/src/CascPort.h index c8145a4512d..a6ee62885b4 100644 --- a/dep/CascLib/src/CascPort.h +++ b/dep/CascLib/src/CascPort.h @@ -13,7 +13,7 @@ #define __CASCPORT_H__ #ifndef __cplusplus - #include <stdbool.h> + #include <stdbool.h> #endif //----------------------------------------------------------------------------- @@ -30,6 +30,7 @@ #define _CRT_SECURE_NO_DEPRECATE #define _CRT_NON_CONFORMING_SWPRINTFS + // Prevent duplicate symbols defined by Windows headers #ifndef WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN #endif @@ -46,7 +47,6 @@ #include <direct.h> #include <malloc.h> #include <windows.h> - #include <ws2tcpip.h> #include <strsafe.h> #define CASCLIB_PLATFORM_LITTLE_ENDIAN diff --git a/dep/CascLib/src/CascReadFile.cpp b/dep/CascLib/src/CascReadFile.cpp index 35f91addf75..3fd173b9bde 100644 --- a/dep/CascLib/src/CascReadFile.cpp +++ b/dep/CascLib/src/CascReadFile.cpp @@ -116,7 +116,7 @@ static DWORD OpenDataStream(TCascFile * hf, PCASC_FILE_SPAN pFileSpan, PCASC_CKE } } -#ifdef _DEBUG +#ifdef CASCLIB_DEBUG static unsigned int table_16C57A8[0x10] = { 0x049396B8, 0x72A82A9B, 0xEE626CCA, 0x9917754F, @@ -182,7 +182,7 @@ static DWORD ParseBlteHeader(PCASC_FILE_SPAN pFileSpan, PCASC_CKEY_ENTRY pCKeyEn if(pEncodedHeader->EncodedSize != pCKeyEntry->EncodedSize) return ERROR_BAD_FORMAT; -#ifdef _DEBUG +#ifdef CASCLIB_DEBUG // Not really needed, it's here just for explanation of what the values mean //assert(memcmp(pCKeyEntry->EKey, pEncodedHeader->EKey.Value, MD5_HASH_SIZE) == 0); VerifyHeaderSpan(pEncodedHeader, HeaderOffset); @@ -203,7 +203,7 @@ static DWORD ParseBlteHeader(PCASC_FILE_SPAN pFileSpan, PCASC_CKEY_ENTRY pCKeyEn { if(pBlteHeader->MustBe0F != 0x0F) return ERROR_BAD_FORMAT; - + // Verify the header size FrameCount = ConvertBytesToInteger_3(pBlteHeader->FrameCount); ExpectedHeaderSize = 0x0C + FrameCount * sizeof(BLTE_FRAME); @@ -587,7 +587,7 @@ static DWORD DecodeFileFrame( switch(pbEncoded[0]) { case 'E': // Encrypted files - + // The work buffer should not have been allocated by any step assert(pbWorkBuffer == NULL && cbWorkBuffer == 0); @@ -613,7 +613,7 @@ static DWORD DecodeFileFrame( break; case 'Z': // ZLIB compressed files - + // If we decompressed less than expected, we simply fill the rest with zeros // Example: INSTALL file from the TACT CASC storage cbDecodedExpected = cbDecoded; @@ -784,7 +784,7 @@ static DWORD ReadFile_WholeFile(TCascFile * hf, LPBYTE pbBuffer) ULONGLONG ByteOffset = pFileSpan->ArchiveOffs + pFileSpan->HeaderSize; DWORD EncodedSize = pCKeyEntry->EncodedSize - pFileSpan->HeaderSize; - // Allocate the buffer for the entire encoded span + // Allocate the buffer for the entire encoded span pbEncodedPtr = pbEncoded = CASC_ALLOC<BYTE>(EncodedSize); if(pbEncoded == NULL) { @@ -1020,6 +1020,29 @@ bool WINAPI CascGetFileInfo(HANDLE hFile, CASC_FILE_INFO_CLASS InfoClass, void * return (pbOutputValue != NULL); } +bool WINAPI CascSetFileFlags(HANDLE hFile, DWORD dwOpenFlags) +{ + TCascFile * hf; + + // Validate the file handle + if((hf = TCascFile::IsValid(hFile)) == NULL) + { + SetCascError(ERROR_INVALID_HANDLE); + return false; + } + + // Currently, only CASC_OVERCOME_ENCRYPTED can be changed + if(dwOpenFlags & ~CASC_OVERCOME_ENCRYPTED) + { + SetCascError(ERROR_INVALID_PARAMETER); + return false; + } + + // Set "overcome encrypted" flag. Will apply on next CascReadFile + hf->bOvercomeEncrypted = (dwOpenFlags & CASC_OVERCOME_ENCRYPTED) != 0; + return true; +} + // // THE FILE SIZE PROBLEM // @@ -1031,7 +1054,7 @@ bool WINAPI CascGetFileInfo(HANDLE hFile, CASC_FILE_INFO_CLASS InfoClass, void * // HotS(29049) ENCODING 0x0024BA45 - 0x0024b98a 0x0024BA45 n/a 0x0024BA45 n/a // HotS(29049) ROOT 0x00193340 - 0x00193340 0x0010db65 0x00193340 0x0010db65 n/a // HotS(29049) (other) 0x00001080 - 0x00001080 0x000008eb 0x00001080 0x000008eb 0x00001080 -// +// // WoW(18888) ENCODING 0x030d487b - 0x030dee79 0x030d487b n/a 0x030d487b n/a // WoW(18888) ROOT 0x016a9800 - n/a 0x0131313d 0x016a9800 0x0131313d n/a // WoW(18888) (other) 0x000007d0 - 0x000007d0 0x00000397 0x000007d0 0x00000397 n/a @@ -1042,6 +1065,13 @@ bool WINAPI CascGetFileSize64(HANDLE hFile, PULONGLONG PtrFileSize) TCascFile * hf; DWORD dwErrCode; + // Validate the file pointer + if(PtrFileSize == NULL) + { + SetCascError(ERROR_INVALID_PARAMETER); + return false; + } + // Validate the file handle if((hf = TCascFile::IsValid(hFile)) == NULL) { @@ -1049,11 +1079,11 @@ bool WINAPI CascGetFileSize64(HANDLE hFile, PULONGLONG PtrFileSize) return false; } - // Validate the file pointer - if(PtrFileSize == NULL) + // If the content key is zeros, we treat the file as a file with size of 0 + if(hf->ContentSize == 0) { - SetCascError(ERROR_INVALID_PARAMETER); - return false; + PtrFileSize[0] = 0; + return true; } // ENCODING on older storages: Content size is not present in the BUILD file @@ -1157,7 +1187,7 @@ DWORD WINAPI CascSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * PtrFilePosHi { ULONGLONG NewPos = 0; LONGLONG DistanceToMove; - + // Assemble the 64-bit distance to move DistanceToMove = (PtrFilePosHigh != NULL) ? MAKE_OFFSET64(PtrFilePosHigh[0], lFilePos) : (LONGLONG)(LONG)lFilePos; @@ -1196,6 +1226,13 @@ bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDW return false; } + // Check files with zero size + if(hf->ContentSize == 0) + { + PtrBytesRead[0] = 0; + return true; + } + // If we don't have file frames loaded, we need to do it now. // Need to do it before file range check, as the file size may be unknown at this point dwErrCode = EnsureFileSpanFramesLoaded(hf); @@ -1242,7 +1279,7 @@ bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDW { // No caching at all. The entire file will be read directly to the user buffer // Used for loading internal files, where we need to read the whole file - case CascCacheNothing: + case CascCacheNothing: dwBytesRead2 = ReadFile_NonCached(hf, pbBuffer, StartOffset, EndOffset); break; @@ -1268,7 +1305,7 @@ bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDW if(PtrBytesRead != NULL) PtrBytesRead[0] = 0; hf->FilePointer = SaveFilePointer; - + // If 0 bytes were requested, it's actually a success return (dwBytesToRead == 0); } diff --git a/dep/CascLib/src/CascRootFile_MNDX.cpp b/dep/CascLib/src/CascRootFile_MNDX.cpp index 9b820c0d639..8075dce3a2b 100644 --- a/dep/CascLib/src/CascRootFile_MNDX.cpp +++ b/dep/CascLib/src/CascRootFile_MNDX.cpp @@ -660,7 +660,7 @@ class TBitEntryArray : public TGenericArray<DWORD> #define INDEX_TO_GROUP(val) (val >> 9) #define GROUP_TO_INDEX(grp) (grp << 9) -// For each 0x200-th bit, this contains information about amount of "1" bits +// For each 0x200-th bit, this contains information about amount of "1" bits typedef struct _BASEVALS { DWORD BaseValue200; // Item value of every 0x200-th item @@ -791,7 +791,7 @@ class TSparseArray if(index & 0x20) IntValue += GetNumberOfSetBits32(ItemBits[(index >> 0x05) - 1]); - // 4) Count the bits in the current DWORD (masked by bit index mask) + // 4) Count the bits in the current DWORD (masked by bit index mask) BitMask = (1 << (index & 0x1F)) - 1; return IntValue + GetNumberOfSetBits32(ItemBits[index >> 0x05] & BitMask); } @@ -888,7 +888,7 @@ class TSparseArray DWORD bitGroup; DWORD edx = index; -#ifdef _DEBUG +#ifdef CASCLIB_DEBUG //if(TotalItemCount > 0x200) //{ // FILE * fp = fopen("e:\\Ladik\\Appdir\\CascLib\\doc\\mndx-sparse-array.txt", "wt"); @@ -1177,7 +1177,7 @@ class TSparseArray return table_1BA1818[bitGroup + distFromBase] + itemIndex; } -#ifdef _DEBUG +#ifdef CASCLIB_DEBUG void Dump(FILE * fp) { size_t * ArrayNormal; @@ -1868,7 +1868,7 @@ class TFileNameDatabase // Get the hasn table item pHashEntry = &HashTable[TableIndex & HashTableMask]; - // + // if(TableIndex == pHashEntry->NextIndex) { // HOTS: 01957BB4 @@ -2467,7 +2467,7 @@ class TFileNameDatabase TSparseArray CollisionTable; // Table of valid collisions, indexed by NodeIndex TSparseArray FileNameIndexes; // Array of file name indexes - TSparseArray CollisionHiBitsIndexes; // Table of indexes of high bits (above 8 bits) for collisions + TSparseArray CollisionHiBitsIndexes; // Table of indexes of high bits (above 8 bits) for collisions // This pair of arrays serves for fast conversion from node index to FragmentOffset / FragmentChar TGenericArray<BYTE> LoBitsTable; // Array of lower 8 bits of name fragment offset diff --git a/dep/CascLib/src/CascRootFile_TVFS.cpp b/dep/CascLib/src/CascRootFile_TVFS.cpp index 52408bf6660..abcac60c5c2 100644 --- a/dep/CascLib/src/CascRootFile_TVFS.cpp +++ b/dep/CascLib/src/CascRootFile_TVFS.cpp @@ -105,8 +105,8 @@ struct TVFS_DIRECTORY_HEADER // In-memory layout of the path table entry typedef struct _TVFS_PATH_TABLE_ENTRY { - LPBYTE pbNamePtr; // Pointer to the begin of the node name - LPBYTE pbNameEnd; // Pointer to the end of the file name + char * m_pNamePtr; // Pointer to the begin of the node name + char * m_pNameEnd; // Pointer to the end of the file name DWORD NodeFlags; // TVFS_PTE_XXX DWORD NodeValue; // Node value } TVFS_PATH_TABLE_ENTRY, *PTVFS_PATH_TABLE_ENTRY; @@ -156,8 +156,8 @@ struct TRootHandler_TVFS : public TFileTreeRoot PathBuffer.AppendChar('/'); // Append the name fragment, if any - if(PathEntry.pbNameEnd > PathEntry.pbNamePtr) - PathBuffer.AppendStringN((const char *)PathEntry.pbNamePtr, (PathEntry.pbNameEnd - PathEntry.pbNamePtr), false); + if(PathEntry.m_pNameEnd > PathEntry.m_pNamePtr) + PathBuffer.AppendStringN(PathEntry.m_pNamePtr, (PathEntry.m_pNameEnd - PathEntry.m_pNamePtr), false); // Append the postfix separator, if needed if(PathEntry.NodeFlags & TVFS_PTE_PATH_SEPARATOR_POST) @@ -312,8 +312,8 @@ struct TRootHandler_TVFS : public TFileTreeRoot LPBYTE CapturePathEntry(TVFS_PATH_TABLE_ENTRY & PathEntry, LPBYTE pbPathTablePtr, LPBYTE pbPathTableEnd) { // Reset the path entry structure - PathEntry.pbNamePtr = pbPathTablePtr; - PathEntry.pbNameEnd = pbPathTablePtr; + PathEntry.m_pNamePtr = (char *)(pbPathTablePtr); + PathEntry.m_pNameEnd = (char *)(pbPathTablePtr); PathEntry.NodeFlags = 0; PathEntry.NodeValue = 0; @@ -332,8 +332,8 @@ struct TRootHandler_TVFS : public TFileTreeRoot if((pbPathTablePtr + nLength) > pbPathTableEnd) return NULL; - PathEntry.pbNamePtr = pbPathTablePtr; - PathEntry.pbNameEnd = pbPathTablePtr + nLength; + PathEntry.m_pNamePtr = (char *)(pbPathTablePtr); + PathEntry.m_pNameEnd = (char *)(pbPathTablePtr + nLength); pbPathTablePtr += nLength; } @@ -536,6 +536,11 @@ struct TRootHandler_TVFS : public TFileTreeRoot } } + //BREAKIF(strcmp((const char *)PathBuffer, "Base") == 0); + //BREAKIF(strcmp((const char *)PathBuffer, "base") == 0); + //BREAKIF(strcmp((const char *)PathBuffer, "base:ComplexTypeDescriptorSizes.dat") == 0); + //BREAKIF(strcmp((const char *)PathBuffer, "DivideAndConquer.w3m:war3map.doo") == 0); + // We need to check whether this is another TVFS directory file if(IsVfsSubDirectory(hs, DirHeader, SubHeader, SpanEntry.EKey, SpanEntry.ContentSize) == ERROR_SUCCESS) { diff --git a/dep/CascLib/src/CascRootFile_WoW.cpp b/dep/CascLib/src/CascRootFile_WoW.cpp index 5b5375bcc7b..85942c432ba 100644 --- a/dep/CascLib/src/CascRootFile_WoW.cpp +++ b/dep/CascLib/src/CascRootFile_WoW.cpp @@ -79,6 +79,31 @@ typedef struct _FILE_ROOT_GROUP } FILE_ROOT_GROUP, *PFILE_ROOT_GROUP; //----------------------------------------------------------------------------- +// Debug local stuff + +#if defined(_MSC_VER) && defined(CASCLIB_DEBUG) +static FILE * fp = NULL; +static bool bLogEntries = true; + +static void LogEntry(DWORD FileDataId, PCONTENT_KEY pCKey) +{ + if(fp && bLogEntries) + { + char szCKey[MD5_STRING_SIZE + 1]; + + if(FileDataId == 1260179) + { + bLogEntries = false; + __debugbreak(); + } + + StringFromBinary(pCKey->Value, MD5_HASH_SIZE, szCKey); + fprintf(fp, "File Data ID: %u, CKey = %s\n", FileDataId, szCKey); + } +} +#endif + +//----------------------------------------------------------------------------- // TRootHandler_WoW interface / implementation #define FTREE_FLAGS_WOW (FTREE_FLAG_USE_DATA_ID | FTREE_FLAG_USE_LOCALE_FLAGS | FTREE_FLAG_USE_CONTENT_FLAGS) @@ -89,7 +114,7 @@ struct TRootHandler_WoW : public TFileTreeRoot TRootHandler_WoW(ROOT_FORMAT RFormat, DWORD HashlessFileCount) : TFileTreeRoot(FTREE_FLAGS_WOW) { - // Turn off the "we know file names" bit + // Turn off the "we know file names" bit FileCounterHashless = HashlessFileCount; FileCounter = 0; RootFormat = RFormat; @@ -155,7 +180,7 @@ struct TRootHandler_WoW : public TFileTreeRoot return pbRootPtr + (sizeof(FILE_ROOT_ENTRY) * RootGroup.Header.NumberOfFiles); case RootFormatWoW82: - + // Verify the position of array of CONTENT_KEY if((pbRootPtr + (sizeof(CONTENT_KEY) * RootGroup.Header.NumberOfFiles)) > pbRootEnd) return NULL; @@ -229,7 +254,6 @@ struct TRootHandler_WoW : public TFileTreeRoot { // Set the file data ID FileDataId = FileDataId + RootGroup.FileDataIds[i]; - //printf("File Data ID: %u\n", FileDataId); // Find the item in the central storage. Insert it to the tree if((pCKeyEntry = FindCKeyEntry_CKey(hs, pCKey->Value)) != NULL) @@ -357,7 +381,7 @@ struct TRootHandler_WoW : public TFileTreeRoot */ DWORD ParseWowRootFile_Level1( - TCascStorage * hs, + TCascStorage * hs, LPBYTE pbRootPtr, LPBYTE pbRootEnd, DWORD dwLocaleMask, @@ -389,7 +413,7 @@ struct TRootHandler_WoW : public TFileTreeRoot if(dwErrCode == ERROR_SUCCESS) dwErrCode = ParseWowRootFile_Level1(hs, pbRootPtr, pbRootEnd, dwLocaleMask, 1); -#ifdef _DEBUG +#ifdef CASCLIB_DEBUG // Dump the array of the file data IDs //FileTree.DumpFileDataIds("e:\\file-data-ids.bin"); #endif @@ -494,6 +518,8 @@ DWORD RootHandler_CreateWoW(TCascStorage * hs, CASC_BLOB & RootFile, DWORD dwLoc pRootHandler = new TRootHandler_WoW(RootFormat, FileCounterHashless); if(pRootHandler != NULL) { + //fp = fopen("E:\\file-data-ids2.txt", "wt"); + // Load the root directory. If load failed, we free the object dwErrCode = pRootHandler->Load(hs, pbRootFile, pbRootEnd, dwLocaleMask); if(dwErrCode != ERROR_SUCCESS) @@ -501,6 +527,8 @@ DWORD RootHandler_CreateWoW(TCascStorage * hs, CASC_BLOB & RootFile, DWORD dwLoc delete pRootHandler; pRootHandler = NULL; } + + //fclose(fp); } // Assign the root directory (or NULL) and return error diff --git a/dep/CascLib/src/DllMain.def b/dep/CascLib/src/DllMain.def index ddda8af921b..e8b4aebd5b4 100644 --- a/dep/CascLib/src/DllMain.def +++ b/dep/CascLib/src/DllMain.def @@ -17,6 +17,7 @@ EXPORTS CascOpenFile CascOpenLocalFile CascGetFileInfo + CascSetFileFlags CascGetFileSize CascGetFileSize64 CascSetFilePointer diff --git a/dep/CascLib/src/common/Array.h b/dep/CascLib/src/common/Array.h index b46f257e8a4..56cdf073a33 100644 --- a/dep/CascLib/src/common/Array.h +++ b/dep/CascLib/src/common/Array.h @@ -104,7 +104,7 @@ class CASC_ARRAY // Make sure we have array large enough if(!EnlargeArray(ItemIndex + 1, true)) return NULL; - + // Get the items range pbLastItem = m_pItemArray + (m_ItemCount * m_ItemSize); pbNewItem = m_pItemArray + (ItemIndex * m_ItemSize); @@ -170,7 +170,7 @@ class CASC_ARRAY m_ItemCountMax = m_ItemCount = m_ItemSize = 0; } -#ifdef _DEBUG +#ifdef CASCLIB_DEBUG size_t BytesAllocated() { return m_ItemCountMax * m_ItemSize; diff --git a/dep/CascLib/src/common/ArraySparse.h b/dep/CascLib/src/common/ArraySparse.h index eb7d478cf3e..b239a0a9e93 100644 --- a/dep/CascLib/src/common/ArraySparse.h +++ b/dep/CascLib/src/common/ArraySparse.h @@ -229,7 +229,7 @@ class CASC_SPARSE_ARRAY return (m_pLevel0 != NULL); } -#ifdef _DEBUG +#ifdef CASCLIB_DEBUG size_t BytesAllocated() { return m_LevelsAllocated * sizeof(CASC_ARRAY_256); diff --git a/dep/CascLib/src/common/FileTree.cpp b/dep/CascLib/src/common/FileTree.cpp index fa21863c5e9..453034c057d 100644 --- a/dep/CascLib/src/common/FileTree.cpp +++ b/dep/CascLib/src/common/FileTree.cpp @@ -13,7 +13,22 @@ #include "../CascCommon.h" //----------------------------------------------------------------------------- -// Local defines +// Local arrays + +static BYTE PathSeparators[256] = +{ +/* 0x00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, +/* 0x30 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x40 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +/* 0x50 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00 + + // Filled by zeros up to 256 bytes +}; + +//----------------------------------------------------------------------------- +// Local functions #define START_ITEM_COUNT 0x4000 @@ -31,6 +46,28 @@ inline void SET_NODE_INT32(void * node, size_t offset, DWORD value) PtrValue[0] = value; } +#ifdef CASCLIB_DEV +//static DWORD dwFileCount = 0; +// +//static void WatchFileNode(PCASC_FILE_NODE pFileNode, const char * szFileName, bool bNewNodeInserted) +//{ +// const char * szSuffix = bNewNodeInserted ? "NEW" : "EXISTING"; +// const char * szFormat = "FileNode %p: CKey: %s, NameHash: %I64x (\"%s\") - %s\n"; +// char szBuffer[MD5_STRING_SIZE + 1]; +// +// // Selected nodes only +// if(dwFileCount < 10 && !_strnicmp(szFileName, "base", 4)) +// { +// printf(szFormat, pFileNode, +// StringFromBinary(pFileNode->pCKeyEntry->CKey, MD5_HASH_SIZE, szBuffer), +// pFileNode->FileNameHash, +// szFileName, +// szSuffix); +// dwFileCount++; +// } +//} +#endif + //----------------------------------------------------------------------------- // Protected functions @@ -91,7 +128,7 @@ PCASC_FILE_NODE CASC_FILE_TREE::InsertNew() } // Insert the node to the map of FileNameHash -> CASC_FILE_NODE -bool CASC_FILE_TREE::InsertToHashTable(PCASC_FILE_NODE pFileNode) +bool CASC_FILE_TREE::InsertToNameMap(PCASC_FILE_NODE pFileNode) { bool bResult = false; @@ -189,7 +226,7 @@ bool CASC_FILE_TREE::RebuildNameMaps() { // Insert it to the map "FileNameHash -> CASC_FILE_NODE" if(pFileNode->FileNameHash != 0) - InsertToHashTable(pFileNode); + InsertToNameMap(pFileNode); // Insert it to the array "FileDataId -> CASC_FILE_NODE" if(FileDataIds.IsInitialized()) @@ -288,13 +325,12 @@ PCASC_FILE_NODE CASC_FILE_TREE::InsertByName(PCASC_CKEY_ENTRY pCKeyEntry, const { PCASC_FILE_NODE pFileNode; ULONGLONG FileNameHash; + //bool bNewNodeInserted = false; // Sanity checks assert(szFileName != NULL && szFileName[0] != 0); assert(pCKeyEntry != NULL); - //BREAK_ON_XKEY3(pCKeyEntry->EKey, 0x00, 0x00, 0x0F); - // Calculate the file name hash FileNameHash = CalcFileNameHash(szFileName); @@ -313,7 +349,7 @@ PCASC_FILE_NODE CASC_FILE_TREE::InsertByName(PCASC_CKEY_ENTRY pCKeyEntry, const SetExtras(pFileNode, FileDataId, LocaleFlags, ContentFlags); // Insert the file node to the hash map - InsertToHashTable(pFileNode); + InsertToNameMap(pFileNode); // Also make sure that it's in the file data id table, if the table is initialized InsertToIdTable(pFileNode); @@ -323,11 +359,16 @@ PCASC_FILE_NODE CASC_FILE_TREE::InsertByName(PCASC_CKEY_ENTRY pCKeyEntry, const // If we created a new node, we need to increment the reference count assert(pCKeyEntry->RefCount != 0xFFFF); + //bNewNodeInserted = true; pCKeyEntry->RefCount++; FileNodes++; } } +#ifdef CASCLIB_DEV + //WatchFileNode(pFileNode, szFileName, bNewNodeInserted); +#endif + return pFileNode; } @@ -349,7 +390,7 @@ PCASC_FILE_NODE CASC_FILE_TREE::InsertByHash(PCASC_CKEY_ENTRY pCKeyEntry, ULONGL pFileNode->FileNameHash = FileNameHash; // Insert the file node to the hash map - InsertToHashTable(pFileNode); + InsertToNameMap(pFileNode); } return pFileNode; @@ -543,13 +584,21 @@ bool CASC_FILE_TREE::SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szF { char chOneChar = szFileName[i]; - // Is there a path separator? - // Note: Warcraft III paths may contain "mount points". - // Example: "frFR-War3Local.mpq:Maps/FrozenThrone/Campaign/NightElfX06Interlude.w3x:war3map.j" - if(chOneChar == '\\' || chOneChar == '/' || chOneChar == ':') + // Is there a path separator, such as '\\' or '/'? + // Also support TVFS "mount points", like "DivideAndConquer.w3m:war3map.doo" + if(PathSeparators[chOneChar]) { + size_t nHashLength = i; + + // If there is a reparse point mark (':'), we need to include it as part of the name + if(PathSeparators[chOneChar] == 0x02) + { + PathBuffer.AppendChar(chOneChar); + nHashLength++; + } + // Calculate hash of the file name up to the end of the node name - FileNameHash = CalcNormNameHash(PathBuffer, i); + FileNameHash = CalcNormNameHash(PathBuffer, nHashLength); // If the entry is not there yet, create new one if((pFolderNode = Find(FileNameHash)) == NULL) @@ -559,95 +608,36 @@ bool CASC_FILE_TREE::SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szF if(pFolderNode == NULL) return false; - // Populate the file entry + // Fill-in flags, name hash and parent + pFolderNode->Flags |= (chOneChar == ':') ? (CFN_FLAG_FOLDER | CFN_FLAG_MOUNT_POINT) : CFN_FLAG_FOLDER; pFolderNode->FileNameHash = FileNameHash; pFolderNode->Parent = Parent; - pFolderNode->Flags |= (chOneChar == ':') ? (CFN_FLAG_FOLDER | CFN_FLAG_MOUNT_POINT) : CFN_FLAG_FOLDER; FolderNodes++; // Set the node sub name to the node SetNodePlainName(pFolderNode, szNodeBegin, szFileName + i); // Insert the entry to the name map - InsertToHashTable(pFolderNode); + InsertToNameMap(pFolderNode); } - // Move the parent to the current node - Parent = (DWORD)NodeTable.IndexOf(pFolderNode); - - // Move the begin of the node after the separator - szNodeBegin = szFileName + i + 1; - } - - // Copy the next character, even if it was slash/backslash before - PathBuffer.AppendChar(AsciiToUpperTable_BkSlash[chOneChar]); - } - - // If anything left, this is gonna be our node name - if(szNodeBegin < szFileName + i) - { - // We need to reset the file node pointer, as the file node table might have changed - pFileNode = (PCASC_FILE_NODE)NodeTable.ItemAt(nFileNode); - - // Write the plain file name to the node - SetNodePlainName(pFileNode, szNodeBegin, szFileName + i); - pFileNode->Parent = Parent; - - // Also insert the node to the hash table so CascOpenFile can find it - if(pFileNode->FileNameHash == 0) - { - pFileNode->FileNameHash = CalcNormNameHash(PathBuffer, i); - InsertToHashTable(pFileNode); - } - } - return true; -} -/* -bool CASC_FILE_TREE::SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szFileName) -{ - ULONGLONG FileNameHash = 0; - PCASC_FILE_NODE pFolderNode = NULL; - LPCSTR szNodeBegin = szFileName; - char szPathBuffer[MAX_PATH+1]; - size_t nFileNode = NodeTable.IndexOf(pFileNode); - size_t i; - DWORD Parent = 0; - - // Sanity checks - assert(szFileName != NULL && szFileName[0] != 0); - - // Traverse the entire path. For each subfolder, we insert an appropriate fake entry - for(i = 0; szFileName[i] != 0; i++) - { - char chOneChar = szFileName[i]; - - // Is there a path separator? - // Note: Warcraft III paths may contain "mount points". - // Example: "frFR-War3Local.mpq:Maps/FrozenThrone/Campaign/NightElfX06Interlude.w3x:war3map.j" - if(chOneChar == '\\' || chOneChar == '/' || chOneChar == ':') - { - // Calculate hash of the file name up to the end of the node name - FileNameHash = CalcNormNameHash(szPathBuffer, i); - - // If the entry is not there yet, create new one - if((pFolderNode = Find(FileNameHash)) == NULL) + // In case we're in the middle a mount point construction (called by CASC_FILE_TREE::InsertByName()), + // then we can get into situation where the call to Find() found the newly constructed item. + // In that case, we just set the name and bail out + else if(pFolderNode == pFileNode) { - // Insert new entry to the tree - pFolderNode = InsertNew(); - if(pFolderNode == NULL) - return false; + // The item must be a mount point, with name hash already set. + assert(pFolderNode->FileNameHash == FileNameHash); + assert(szFileName[i + 1] == 0); - // Populate the file entry - pFolderNode->FileNameHash = FileNameHash; + // Fill-in the flags and parent + pFolderNode->Flags |= (CFN_FLAG_FOLDER | CFN_FLAG_MOUNT_POINT); pFolderNode->Parent = Parent; - pFolderNode->Flags |= (chOneChar == ':') ? (CFN_FLAG_FOLDER | CFN_FLAG_MOUNT_POINT) : CFN_FLAG_FOLDER; FolderNodes++; // Set the node sub name to the node SetNodePlainName(pFolderNode, szNodeBegin, szFileName + i); - - // Insert the entry to the name map - InsertToHashTable(pFolderNode); + return true; } // Move the parent to the current node @@ -655,10 +645,16 @@ bool CASC_FILE_TREE::SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szF // Move the begin of the node after the separator szNodeBegin = szFileName + i + 1; + + // If the separator character was already appended, skip the rest of the loop + if(PathSeparators[chOneChar] == 0x02) + { + continue; + } } - // Copy the next character, even if it was slash/backslash before - szPathBuffer[i] = AsciiToUpperTable_BkSlash[chOneChar]; + // Append the character, if not appended yet + PathBuffer.AppendChar(AsciiToUpperTable_BkSlash[chOneChar]); } // If anything left, this is gonna be our node name @@ -674,13 +670,13 @@ bool CASC_FILE_TREE::SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szF // Also insert the node to the hash table so CascOpenFile can find it if(pFileNode->FileNameHash == 0) { - pFileNode->FileNameHash = CalcNormNameHash(szPathBuffer, i); - InsertToHashTable(pFileNode); + pFileNode->FileNameHash = CalcNormNameHash(PathBuffer, i); + InsertToNameMap(pFileNode); } } return true; } -*/ + size_t CASC_FILE_TREE::GetMaxFileIndex() { if(FileDataIds.IsInitialized()) diff --git a/dep/CascLib/src/common/FileTree.h b/dep/CascLib/src/common/FileTree.h index 69a2c15c496..0e178c769bb 100644 --- a/dep/CascLib/src/common/FileTree.h +++ b/dep/CascLib/src/common/FileTree.h @@ -82,7 +82,7 @@ class CASC_FILE_TREE // Retrieve the maximum FileDataId ever inserted DWORD GetNextFileDataId(); -#ifdef _DEBUG +#ifdef CASCLIB_DEBUG void DumpFileDataIds(const char * szFileName) { FileDataIds.Dump(szFileName); @@ -93,7 +93,7 @@ class CASC_FILE_TREE PCASC_FILE_NODE InsertNew(PCASC_CKEY_ENTRY pCKeyEntry); PCASC_FILE_NODE InsertNew(); - bool InsertToHashTable(PCASC_FILE_NODE pFileNode); + bool InsertToNameMap(PCASC_FILE_NODE pFileNode); bool InsertToIdTable(PCASC_FILE_NODE pFileNode); bool SetNodePlainName(PCASC_FILE_NODE pFileNode, const char * szPlainName, const char * szPlainNameEnd); diff --git a/dep/CascLib/src/common/ListFile.cpp b/dep/CascLib/src/common/ListFile.cpp index 4545ada62e9..05ac48c4cbd 100644 --- a/dep/CascLib/src/common/ListFile.cpp +++ b/dep/CascLib/src/common/ListFile.cpp @@ -62,14 +62,16 @@ static char * ListFile_SkipSpaces(PLISTFILE_CACHE pCache) static void ListFile_CheckFormat(PLISTFILE_CACHE pCache) { - // Only if the listfile is greatger than 2 MB - if((pCache->pEnd - pCache->pBegin) > 0x100000) + const size_t nSizeLimit = 0x20; + + // Only if the listfile is greater than 2 MB + if((pCache->pEnd - pCache->pBegin) > nSizeLimit) { char * szPtr = pCache->pBegin; size_t nDigitCount = 0; // Calculate the amount of digits - while(nDigitCount <= 20 && '0' <= szPtr[nDigitCount] && szPtr[nDigitCount] <= '9') + while(nDigitCount <= nSizeLimit && '0' <= szPtr[nDigitCount] && szPtr[nDigitCount] <= '9') nDigitCount++; // There must be a semicolon after diff --git a/dep/CascLib/src/common/Mime.cpp b/dep/CascLib/src/common/Mime.cpp index 5aa7401ff24..d3d975ff00e 100644 --- a/dep/CascLib/src/common/Mime.cpp +++ b/dep/CascLib/src/common/Mime.cpp @@ -331,7 +331,7 @@ DWORD CASC_MIME_ELEMENT::Load(char * mime_data_begin, char * mime_data_end, cons // Otherwise, we decode the data to the end of the document if(boundary_ptr != NULL) { - // Find the end of the current data by the boundary. It is 2 characters before the next boundary + // Find the end of the current data by the boundary. It is 2 characters before the next boundary content.end = strstr(content.ptr, boundary_ptr); if(content.end == NULL) return ERROR_BAD_FORMAT; @@ -362,7 +362,7 @@ DWORD CASC_MIME_ELEMENT::Load(char * mime_data_begin, char * mime_data_end, cons case MimeEncodingBase64: dwErrCode = DecodeBase64(content.ptr, content.end, data); break; - + default: dwErrCode = ERROR_NOT_SUPPORTED; assert(false); @@ -379,7 +379,7 @@ DWORD CASC_MIME_ELEMENT::Load(char * mime_data_begin, char * mime_data_end, cons return dwErrCode; } -#ifdef _DEBUG +#ifdef CASCLIB_DEBUG #define MAX_LEVEL 0x10 void CASC_MIME_ELEMENT::Print(size_t nLevel, size_t nIndex) { @@ -473,7 +473,7 @@ bool CASC_MIME_ELEMENT::ExtractBoundary(const char * line) { // Set begin of the boundary begin = begin + 10; - + // Is there also end? if((end = strchr(begin, '\"')) != NULL) { @@ -647,7 +647,7 @@ DWORD CASC_MIME::Load(char * data, CASC_MIME_RESPONSE & MimeResponse) return ERROR_BAD_FORMAT; // Debug: dump the MIME data to file -#ifdef _DEBUG +#ifdef CASCLIB_DEBUG //CascDumpData("E:\\mime_raw_data.txt", data, MimeResponse.response_length); #endif @@ -667,7 +667,7 @@ DWORD CASC_MIME::Load(char * data, CASC_MIME_RESPONSE & MimeResponse) return root.Load(data, data + MimeResponse.response_length); } -#ifdef _DEBUG +#ifdef CASCLIB_DEBUG void CASC_MIME::Print() { root.Print(0, 0); diff --git a/dep/CascLib/src/common/Mime.h b/dep/CascLib/src/common/Mime.h index 9142c33bfd3..af204352263 100644 --- a/dep/CascLib/src/common/Mime.h +++ b/dep/CascLib/src/common/Mime.h @@ -92,7 +92,7 @@ class CASC_MIME_ELEMENT CASC_MIME_ELEMENT * GetChild() { return folder.pChild; } -#ifdef _DEBUG +#ifdef CASCLIB_DEBUG void Print(size_t nLevel, size_t nIndex); #endif @@ -127,7 +127,7 @@ class CASC_MIME DWORD Load(char * data, CASC_MIME_RESPONSE & MimeResponse); -#ifdef _DEBUG +#ifdef CASCLIB_DEBUG void Print(); #endif diff --git a/dep/CascLib/src/common/RootHandler.cpp b/dep/CascLib/src/common/RootHandler.cpp index 95fa106e1bd..227eaff6b96 100644 --- a/dep/CascLib/src/common/RootHandler.cpp +++ b/dep/CascLib/src/common/RootHandler.cpp @@ -83,8 +83,8 @@ PCASC_CKEY_ENTRY TFileTreeRoot::Search(TCascSearch * pSearch, PCASC_FIND_DATA pF pFileNode = FileTree.PathAt(pFindData->szFileName, MAX_PATH, pSearch->nFileIndex++); if(pFileNode != NULL) { - // Ignore folders and mount points - if(!(pFileNode->Flags & CFN_FLAG_FOLDER)) + // Ignore folders, but report mount points. These can and should be able to open and read + if((pFileNode->Flags & (CFN_FLAG_FOLDER | CFN_FLAG_MOUNT_POINT)) != CFN_FLAG_FOLDER) { // Check the wildcard if(CascCheckWildCard(pFindData->szFileName, pSearch->szMask)) diff --git a/dep/CascLib/src/common/Sockets.cpp b/dep/CascLib/src/common/Sockets.cpp index bb490505b15..8eb9680d448 100644 --- a/dep/CascLib/src/common/Sockets.cpp +++ b/dep/CascLib/src/common/Sockets.cpp @@ -13,14 +13,35 @@ #include "../CascLib.h" #include "../CascCommon.h" +#ifdef CASCLIB_PLATFORM_WINDOWS +#include <ws2tcpip.h> +#endif + //----------------------------------------------------------------------------- // Local variables #define BUFFER_INITIAL_SIZE 0x8000 +#ifndef INVALID_SOCKET +#define INVALID_SOCKET (SOCKET)(-1) // Not defined in Linux +#endif + CASC_SOCKET_CACHE SocketCache; //----------------------------------------------------------------------------- +// Conversion functions + +static SOCKET inline HandleToSocket(HANDLE sock) +{ + return (SOCKET)(intptr_t)(sock); +} + +static HANDLE inline SocketToHandle(SOCKET sock) +{ + return (HANDLE)(intptr_t)(sock); +} + +//----------------------------------------------------------------------------- // CASC_SOCKET functions // Guarantees that there is zero terminator after the response @@ -42,10 +63,10 @@ char * CASC_SOCKET::ReadResponse(const char * request, size_t request_length, CA // Send the request to the remote host. On Linux, this call may send signal(SIGPIPE), // we need to prevend that by using the MSG_NOSIGNAL flag. On Windows, it fails normally. - while(send(sock, request, (int)request_length, MSG_NOSIGNAL) == SOCKET_ERROR) + while(send(HandleToSocket(sock), request, (int)request_length, MSG_NOSIGNAL) == SOCKET_ERROR) { // If the connection was closed by the remote host, we try to reconnect - if(ReconnectAfterShutdown(sock, remoteItem) == INVALID_SOCKET) + if(ReconnectAfterShutdown(sock, remoteItem) == SocketToHandle(INVALID_SOCKET)) { SetCascError(ERROR_NETWORK_NOT_AVAILABLE); CascUnlock(Lock); @@ -74,7 +95,7 @@ char * CASC_SOCKET::ReadResponse(const char * request, size_t request_length, CA // Receive the next part of the response, up to buffer size // Return value 0 means "connection closed", -1 means an error - bytes_received = recv(sock, server_response + total_received, (int)(buffer_length - total_received), 0); + bytes_received = recv(HandleToSocket(sock), server_response + total_received, (int)(buffer_length - total_received), 0); if(bytes_received <= 0) { MimeResponse.ParseResponse(server_response, total_received, true); @@ -192,28 +213,28 @@ DWORD CASC_SOCKET::GetAddrInfoWrapper(const char * hostName, unsigned portNum, P } } -SOCKET CASC_SOCKET::CreateAndConnect(addrinfo * remoteItem) +HANDLE CASC_SOCKET::CreateAndConnect(PADDRINFO remoteItem) { SOCKET sock; // Create new socket - // On error, returns returns INVALID_SOCKET (-1) on Windows, -1 on Linux + // On error, returns returns INVALID_SOCKET (0 on Windows, -1 on Linux) if((sock = socket(remoteItem->ai_family, remoteItem->ai_socktype, remoteItem->ai_protocol)) > 0) { // Connect to the remote host // On error, returns SOCKET_ERROR (-1) on Windows, -1 on Linux if(connect(sock, remoteItem->ai_addr, (int)remoteItem->ai_addrlen) == 0) - return sock; + return SocketToHandle(sock); - // Failed. Close the socket and return 0 + // Failed. Close the socket and return INVALID_SOCKET closesocket(sock); sock = INVALID_SOCKET; } - return sock; + return SocketToHandle(sock); } -SOCKET CASC_SOCKET::ReconnectAfterShutdown(SOCKET & sock, addrinfo * remoteItem) +HANDLE CASC_SOCKET::ReconnectAfterShutdown(HANDLE & sock, PADDRINFO remoteItem) { // Retrieve the error code related to previous socket operation switch(GetSockError()) @@ -222,8 +243,8 @@ SOCKET CASC_SOCKET::ReconnectAfterShutdown(SOCKET & sock, addrinfo * remoteItem) case WSAECONNRESET: // Windows { // Close the old socket - if(sock != INVALID_SOCKET) - closesocket(sock); + if(sock != SocketToHandle(INVALID_SOCKET)) + closesocket(HandleToSocket(sock)); // Attempt to reconnect sock = CreateAndConnect(remoteItem); @@ -232,10 +253,10 @@ SOCKET CASC_SOCKET::ReconnectAfterShutdown(SOCKET & sock, addrinfo * remoteItem) } // Another problem - return INVALID_SOCKET; + return SocketToHandle(INVALID_SOCKET); } -PCASC_SOCKET CASC_SOCKET::New(addrinfo * remoteList, addrinfo * remoteItem, const char * hostName, unsigned portNum, SOCKET sock) +PCASC_SOCKET CASC_SOCKET::New(PADDRINFO remoteList, PADDRINFO remoteItem, const char * hostName, unsigned portNum, HANDLE sock) { PCASC_SOCKET pSocket; size_t length = strlen(hostName); @@ -268,7 +289,7 @@ PCASC_SOCKET CASC_SOCKET::Connect(const char * hostName, unsigned portNum) addrinfo * remoteList; addrinfo * remoteItem; addrinfo hints = {0}; - SOCKET sock; + HANDLE sock; int nErrCode; // Retrieve the information about the remote host @@ -293,7 +314,7 @@ PCASC_SOCKET CASC_SOCKET::Connect(const char * hostName, unsigned portNum) } // Close the socket - closesocket(sock); + closesocket(HandleToSocket(sock)); } } @@ -316,7 +337,7 @@ void CASC_SOCKET::Delete() // Close the socket, if any if(sock != 0) - closesocket(sock); + closesocket(HandleToSocket(sock)); sock = 0; // Free the lock diff --git a/dep/CascLib/src/common/Sockets.h b/dep/CascLib/src/common/Sockets.h index c821748802b..917be387234 100644 --- a/dep/CascLib/src/common/Sockets.h +++ b/dep/CascLib/src/common/Sockets.h @@ -14,10 +14,6 @@ //----------------------------------------------------------------------------- // Defines -#ifndef INVALID_SOCKET -#define INVALID_SOCKET (SOCKET)(-1) -#endif - #ifndef SOCKET_ERROR #define SOCKET_ERROR (-1) #endif @@ -57,9 +53,9 @@ class CASC_SOCKET // Constructor and destructor static int GetSockError(); static DWORD GetAddrInfoWrapper(const char * hostName, unsigned portNum, PADDRINFO hints, PADDRINFO * ppResult); - static SOCKET CreateAndConnect(addrinfo * remoteItem); - static SOCKET ReconnectAfterShutdown(SOCKET & sock, addrinfo * remoteItem); - static PCASC_SOCKET New(addrinfo * remoteList, addrinfo * remoteItem, const char * hostName, unsigned portNum, SOCKET sock); + static HANDLE CreateAndConnect(PADDRINFO remoteItem); + static HANDLE ReconnectAfterShutdown(HANDLE & sock, PADDRINFO remoteItem); + static PCASC_SOCKET New(PADDRINFO remoteList, PADDRINFO remoteItem, const char * hostName, unsigned portNum, HANDLE sock); static PCASC_SOCKET Connect(const char * hostName, unsigned portNum); // Frees all resources and deletes the socket @@ -76,7 +72,7 @@ class CASC_SOCKET PADDRINFO remoteList; // List of the remote host informations PADDRINFO remoteItem; // The particular host picked during the last connection attempt CASC_LOCK Lock; // Lock for single threaded access - SOCKET sock; // Opened and connected socket + HANDLE sock; // Opened and connected socket DWORD dwRefCount; // Number of references DWORD portNum; // Port number char hostName[1]; // Buffer for storing remote host (variable length) |