This commit is contained in:
Shauren
2023-06-23 10:25:18 +02:00
parent 54596eb8ce
commit 9ad1e5d635
25 changed files with 325 additions and 215 deletions

View File

@@ -62,7 +62,7 @@ target_include_directories(casc
PRIVATE
${CMAKE_SOURCE_DIR}/dep)
target_compile_definitions(casc PUBLIC -D__SYS_ZLIB -DCASCLIB_NO_AUTO_LINK_LIBRARY)
target_compile_definitions(casc PUBLIC -D__SYS_ZLIB -DCASCLIB_NO_AUTO_LINK_LIBRARY -DCASCLIB_NODEBUG)
target_link_libraries(casc
PRIVATE

View File

@@ -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);

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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"
//

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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)
{

View File

@@ -78,6 +78,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
@@ -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

View File

@@ -17,6 +17,7 @@ EXPORTS
CascOpenFile
CascOpenLocalFile
CascGetFileInfo
CascSetFileFlags
CascGetFileSize
CascGetFileSize64
CascSetFilePointer

View File

@@ -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;

View File

@@ -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);

View File

@@ -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,17 +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);
}
// 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)
{
// The item must be a mount point, with name hash already set.
assert(pFolderNode->FileNameHash == FileNameHash);
assert(szFileName[i + 1] == 0);
// Fill-in the flags and parent
pFolderNode->Flags |= (CFN_FLAG_FOLDER | CFN_FLAG_MOUNT_POINT);
pFolderNode->Parent = Parent;
FolderNodes++;
// Set the node sub name to the node
SetNodePlainName(pFolderNode, szNodeBegin, szFileName + i);
return true;
}
// Move the parent to the current node
@@ -577,9 +645,15 @@ 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
// Append the character, if not appended yet
PathBuffer.AppendChar(AsciiToUpperTable_BkSlash[chOneChar]);
}
@@ -597,90 +671,12 @@ bool CASC_FILE_TREE::SetNodeFileName(PCASC_FILE_NODE pFileNode, const char * szF
if(pFileNode->FileNameHash == 0)
{
pFileNode->FileNameHash = CalcNormNameHash(PathBuffer, i);
InsertToHashTable(pFileNode);
InsertToNameMap(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)
{
// Insert new entry to the tree
pFolderNode = InsertNew();
if(pFolderNode == NULL)
return false;
// Populate the file entry
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);
}
// 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
szPathBuffer[i] = 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(szPathBuffer, i);
InsertToHashTable(pFileNode);
}
}
return true;
}
*/
size_t CASC_FILE_TREE::GetMaxFileIndex()
{
if(FileDataIds.IsInitialized())

View File

@@ -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);

View File

@@ -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

View File

@@ -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);

View File

@@ -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

View File

@@ -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))

View File

@@ -13,13 +13,34 @@
#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
@@ -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

View File

@@ -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)

View File

@@ -63,7 +63,7 @@ catch2
CascLib (An open-source implementation of library for reading CASC storage from Blizzard games since 2014)
https://github.com/ladislav-zezula/CascLib
Version: a5080b5794027a25d98aa6024b2bef17d06fe0ea
Version: ebd79e8fd43279343c543a27fce620f6b1b53cb9
rapidjson (A fast JSON parser/generator for C++ with both SAX/DOM style API http://rapidjson.org/)
https://github.com/Tencent/rapidjson