This commit is contained in:
Shauren
2023-02-06 20:08:39 +01:00
parent 9932046499
commit fd154940ed
39 changed files with 2116 additions and 1675 deletions

View File

@@ -61,7 +61,8 @@ TCascStorage::TCascStorage()
pRootHandler = NULL;
dwRefCount = 1;
szRootPath = szDataPath = szIndexPath = szBuildFile = szCdnServers = szCdnPath = szCdnHostUrl = szCodeName = NULL;
szRootPath = szDataPath = szIndexPath = szFilesPath = szConfigPath = szMainFile = NULL;
szCdnHostUrl = szCdnServers = szCdnPath = szCodeName = NULL;
szIndexFormat = NULL;
szRegion = NULL;
szBuildKey = NULL;
@@ -100,14 +101,16 @@ TCascStorage::~TCascStorage()
CascFreeLock(StorageLock);
// Free the file paths
CASC_FREE(szDataPath);
CASC_FREE(szRootPath);
CASC_FREE(szBuildFile);
CASC_FREE(szDataPath);
CASC_FREE(szIndexPath);
CASC_FREE(szFilesPath);
CASC_FREE(szConfigPath);
CASC_FREE(szMainFile);
CASC_FREE(szCdnHostUrl);
CASC_FREE(szCdnServers);
CASC_FREE(szCdnPath);
CASC_FREE(szCodeName);
CASC_FREE(szCdnHostUrl);
CASC_FREE(szRegion);
CASC_FREE(szBuildKey);
@@ -166,20 +169,6 @@ void * ProbeOutputBuffer(void * pvBuffer, size_t cbLength, size_t cbMinLength, s
return pvBuffer;
}
static LPTSTR CheckForIndexDirectory(TCascStorage * hs, LPCTSTR szSubDir)
{
TCHAR szIndexPath[MAX_PATH];
// Combine the index path
CombinePath(szIndexPath, _countof(szIndexPath), hs->szDataPath, szSubDir, NULL);
// Check whether the path exists
if(!DirectoryExists(szIndexPath))
return NULL;
return CascNewStr(szIndexPath);
}
// Inserts an entry from the text build file
static PCASC_CKEY_ENTRY InsertCKeyEntry(TCascStorage * hs, CASC_CKEY_ENTRY & CKeyEntry)
{
@@ -451,8 +440,7 @@ static int LoadEncodingCKeyPage(TCascStorage * hs, CASC_ENCODING_HEADER & EnHead
static int LoadEncodingManifest(TCascStorage * hs)
{
CASC_CKEY_ENTRY & CKeyEntry = hs->EncodingCKey;
LPBYTE pbEncodingFile;
DWORD cbEncodingFile = 0;
CASC_BLOB EncodingFile;
DWORD dwErrCode = ERROR_SUCCESS;
// Inform the user about what we are doing
@@ -465,24 +453,25 @@ static int LoadEncodingManifest(TCascStorage * hs)
InsertCKeyEntry(hs, CKeyEntry);
// Load the entire encoding file to memory
pbEncodingFile = LoadInternalFileToMemory(hs, &hs->EncodingCKey, &cbEncodingFile);
if(pbEncodingFile != NULL && cbEncodingFile != 0)
dwErrCode = LoadInternalFileToMemory(hs, &hs->EncodingCKey, EncodingFile);
if(dwErrCode == ERROR_SUCCESS && EncodingFile.cbData != 0)
{
CASC_ENCODING_HEADER EnHeader;
// Capture the header of the ENCODING file
dwErrCode = CaptureEncodingHeader(EnHeader, pbEncodingFile, cbEncodingFile);
dwErrCode = CaptureEncodingHeader(EnHeader, EncodingFile.pbData, EncodingFile.cbData);
if(dwErrCode == ERROR_SUCCESS)
{
// Get the CKey page header and the first page
PFILE_CKEY_PAGE pPageHeader = (PFILE_CKEY_PAGE)(pbEncodingFile + sizeof(FILE_ENCODING_HEADER) + EnHeader.ESpecBlockSize);
PFILE_CKEY_PAGE pPageHeader = (PFILE_CKEY_PAGE)(EncodingFile.pbData + sizeof(FILE_ENCODING_HEADER) + EnHeader.ESpecBlockSize);
LPBYTE pbEncodingEnd = EncodingFile.pbData + EncodingFile.cbData;
LPBYTE pbCKeyPage = (LPBYTE)(pPageHeader + EnHeader.CKeyPageCount);
// Go through all CKey pages and verify them
for(DWORD i = 0; i < EnHeader.CKeyPageCount; i++)
{
// Check if there is enough space in the buffer
if((pbCKeyPage + EnHeader.CKeyPageSize) > (pbEncodingFile + cbEncodingFile))
if((pbCKeyPage + EnHeader.CKeyPageSize) > pbEncodingEnd)
{
dwErrCode = ERROR_FILE_CORRUPT;
break;
@@ -519,9 +508,6 @@ static int LoadEncodingManifest(TCascStorage * hs)
{
dwErrCode = CopyBuildFileItemsToCKeyArray(hs);
}
// Free the loaded ENCODING file
CASC_FREE(pbEncodingFile);
}
else
{
@@ -735,15 +721,18 @@ static int LoadDownloadManifest(TCascStorage * hs, CASC_DOWNLOAD_HEADER & DlHead
// Insert the entry to the central CKey table
if((pCKeyEntry = InsertCKeyEntry(hs, DlEntry)) != NULL)
{
// Supply the tag bits
for(size_t j = 0; j < TagItemCount; j++)
if(TagArray != NULL)
{
// Set the bit in the entry, if the tag for it is present
if((BitMaskOffset < TagArray[j].BitmapLength) && (TagArray[j].Bitmap[BitMaskOffset] & BitMaskBit))
pCKeyEntry->TagBitMask |= TagBit;
// Supply the tag bits
for(size_t j = 0; j < TagItemCount; j++)
{
// Set the bit in the entry, if the tag for it is present
if((BitMaskOffset < TagArray[j].BitmapLength) && (TagArray[j].Bitmap[BitMaskOffset] & BitMaskBit))
pCKeyEntry->TagBitMask |= TagBit;
// Move to the next bit
TagBit <<= 1;
// Move to the next bit
TagBit <<= 1;
}
}
}
@@ -762,8 +751,7 @@ static int LoadDownloadManifest(TCascStorage * hs, CASC_DOWNLOAD_HEADER & DlHead
static int LoadDownloadManifest(TCascStorage * hs)
{
PCASC_CKEY_ENTRY pCKeyEntry = FindCKeyEntry_CKey(hs, hs->DownloadCKey.CKey);
LPBYTE pbDownloadFile = NULL;
DWORD cbDownloadFile = 0;
CASC_BLOB DownloadFile;
DWORD dwErrCode = ERROR_SUCCESS;
// Inform the user about what we are doing
@@ -771,21 +759,18 @@ static int LoadDownloadManifest(TCascStorage * hs)
return ERROR_CANCELLED;
// Load the entire DOWNLOAD file to memory
pbDownloadFile = LoadInternalFileToMemory(hs, pCKeyEntry, &cbDownloadFile);
if(pbDownloadFile != NULL && cbDownloadFile != 0)
dwErrCode = LoadInternalFileToMemory(hs, pCKeyEntry, DownloadFile);
if(dwErrCode == ERROR_SUCCESS && DownloadFile.cbData != 0)
{
CASC_DOWNLOAD_HEADER DlHeader;
// Capture the header of the DOWNLOAD file
dwErrCode = CaptureDownloadHeader(DlHeader, pbDownloadFile, cbDownloadFile);
dwErrCode = CaptureDownloadHeader(DlHeader, DownloadFile.pbData, DownloadFile.cbData);
if(dwErrCode == ERROR_SUCCESS)
{
// Parse the entire download manifest
dwErrCode = LoadDownloadManifest(hs, DlHeader, pbDownloadFile, pbDownloadFile + cbDownloadFile);
dwErrCode = LoadDownloadManifest(hs, DlHeader, DownloadFile.pbData, DownloadFile.pbData + DownloadFile.cbData);
}
// Free the loaded manifest
CASC_FREE(pbDownloadFile);
}
// If the DOWNLOAD manifest is not present, we won't abort the downloading process.
@@ -799,8 +784,7 @@ static int LoadDownloadManifest(TCascStorage * hs)
static int LoadInstallManifest(TCascStorage * hs)
{
PCASC_CKEY_ENTRY pCKeyEntry = FindCKeyEntry_CKey(hs, hs->InstallCKey.CKey);
LPBYTE pbInstallFile = NULL;
DWORD cbInstallFile = 0;
CASC_BLOB InstallFile;
DWORD dwErrCode = ERROR_SUCCESS;
// Inform the user about what we are doing
@@ -808,11 +792,10 @@ static int LoadInstallManifest(TCascStorage * hs)
return ERROR_CANCELLED;
// Load the entire DOWNLOAD file to memory
pbInstallFile = LoadInternalFileToMemory(hs, pCKeyEntry, &cbInstallFile);
if(pbInstallFile != NULL && cbInstallFile != 0)
dwErrCode = LoadInternalFileToMemory(hs, pCKeyEntry, InstallFile);
if(dwErrCode == ERROR_SUCCESS && InstallFile.cbData != 0)
{
dwErrCode = RootHandler_CreateInstall(hs, pbInstallFile, cbInstallFile);
CASC_FREE(pbInstallFile);
dwErrCode = RootHandler_CreateInstall(hs, InstallFile);
}
else
{
@@ -864,9 +847,8 @@ static int LoadBuildManifest(TCascStorage * hs, DWORD dwLocaleMask)
{
PCASC_CKEY_ENTRY pCKeyEntry = &hs->RootFile;
TRootHandler * pOldRootHandler = NULL;
CASC_BLOB RootFile;
PDWORD FileSignature;
LPBYTE pbRootFile = NULL;
DWORD cbRootFile = 0;
DWORD dwErrCode = ERROR_BAD_FORMAT;
// Sanity checks
@@ -888,30 +870,30 @@ __LoadRootFile:
// Load the entire ROOT file to memory
pCKeyEntry = FindCKeyEntry_CKey(hs, pCKeyEntry->CKey);
pbRootFile = LoadInternalFileToMemory(hs, pCKeyEntry, &cbRootFile);
if(pbRootFile != NULL)
dwErrCode = LoadInternalFileToMemory(hs, pCKeyEntry, RootFile);
if(dwErrCode == ERROR_SUCCESS)
{
// Ignore ROOT files that contain just a MD5 hash
if(cbRootFile > MD5_STRING_SIZE)
if(RootFile.cbData > MD5_STRING_SIZE)
{
// Check the type of the ROOT file
FileSignature = (PDWORD)pbRootFile;
FileSignature = (PDWORD)(RootFile.pbData);
switch(FileSignature[0])
{
case CASC_MNDX_ROOT_SIGNATURE:
dwErrCode = RootHandler_CreateMNDX(hs, pbRootFile, cbRootFile);
dwErrCode = RootHandler_CreateMNDX(hs, RootFile);
break;
case CASC_DIABLO3_ROOT_SIGNATURE:
dwErrCode = RootHandler_CreateDiablo3(hs, pbRootFile, cbRootFile);
dwErrCode = RootHandler_CreateDiablo3(hs, RootFile);
break;
case CASC_TVFS_ROOT_SIGNATURE:
dwErrCode = RootHandler_CreateTVFS(hs, pbRootFile, cbRootFile);
dwErrCode = RootHandler_CreateTVFS(hs, RootFile);
break;
case CASC_WOW82_ROOT_SIGNATURE:
dwErrCode = RootHandler_CreateWoW(hs, pbRootFile, cbRootFile, dwLocaleMask);
dwErrCode = RootHandler_CreateWoW(hs, RootFile, dwLocaleMask);
break;
default:
@@ -921,21 +903,18 @@ __LoadRootFile:
// If the format was not recognized, they need to return ERROR_BAD_FORMAT
//
dwErrCode = RootHandler_CreateOverwatch(hs, pbRootFile, cbRootFile);
dwErrCode = RootHandler_CreateOverwatch(hs, RootFile);
if(dwErrCode == ERROR_BAD_FORMAT)
{
dwErrCode = RootHandler_CreateStarcraft1(hs, pbRootFile, cbRootFile);
dwErrCode = RootHandler_CreateStarcraft1(hs, RootFile);
if(dwErrCode == ERROR_BAD_FORMAT)
{
dwErrCode = RootHandler_CreateWoW(hs, pbRootFile, cbRootFile, dwLocaleMask);
dwErrCode = RootHandler_CreateWoW(hs, RootFile, dwLocaleMask);
}
}
break;
}
}
// Free the root file
CASC_FREE(pbRootFile);
}
else
{
@@ -1109,73 +1088,7 @@ static bool GetStoragePathProduct(TCascStorage * hs, void * pvStorageInfo, size_
return (szBuffer != NULL);
}
static DWORD InitializeLocalDirectories(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs)
{
LPTSTR szWorkPath;
DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
// Find the root directory of the storage. The root directory
// is the one with ".build.info" or ".build.db".
szWorkPath = CascNewStr(pArgs->szLocalPath);
if(szWorkPath != NULL)
{
// Get the length and go up until we find the ".build.info" or ".build.db"
for(;;)
{
// Is this a game directory?
dwErrCode = CheckGameDirectory(hs, szWorkPath);
if(dwErrCode == ERROR_SUCCESS)
{
dwErrCode = ERROR_SUCCESS;
break;
}
// Cut one path part
if(!CutLastPathPart(szWorkPath))
{
dwErrCode = ERROR_FILE_NOT_FOUND;
break;
}
}
// Find the index directory
if(dwErrCode == ERROR_SUCCESS)
{
// First, check for more common "data" subdirectory
if((hs->szIndexPath = CheckForIndexDirectory(hs, _T("data"))) != NULL)
dwErrCode = ERROR_SUCCESS;
// Second, try the "darch" subdirectory (older builds of HOTS - Alpha)
else if((hs->szIndexPath = CheckForIndexDirectory(hs, _T("darch"))) != NULL)
dwErrCode = ERROR_SUCCESS;
else
dwErrCode = ERROR_FILE_NOT_FOUND;
}
// Free the work path buffer
CASC_FREE(szWorkPath);
}
return dwErrCode;
}
static DWORD InitializeOnlineDirectories(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs)
{
// Create the root path
hs->szRootPath = CascNewStr(pArgs->szLocalPath);
if(hs->szRootPath != NULL)
{
hs->BuildFileType = CascVersionsDb;
hs->dwFeatures |= CASC_FEATURE_ONLINE;
hs->dwFeatures |= (pArgs->dwFlags & (CASC_FEATURE_LOCAL_CDNS | CASC_FEATURE_LOCAL_VERSIONS));
return ERROR_SUCCESS;
}
return ERROR_NOT_ENOUGH_MEMORY;
}
static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs)
static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs, LPCTSTR szMainFile, CBLD_TYPE BuildFileType, DWORD dwFeatures)
{
LPCTSTR szCdnHostUrl = NULL;
LPCTSTR szCodeName = NULL;
@@ -1206,21 +1119,48 @@ static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs)
if(ExtractVersionedArgument(pArgs, FIELD_OFFSET(CASC_OPEN_STORAGE_ARGS, szBuildKey), &szBuildKey) && szBuildKey != NULL)
hs->szBuildKey = CascNewStrT2A(szBuildKey);
// Special handling to online storages
if(hs->dwFeatures & CASC_FEATURE_ONLINE)
{
// Enable caching of the sockets. This will add references
// to all existing and all future sockets
sockets_set_caching(true);
// 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->BuildFileType = BuildFileType;
// For online storages, we need to load CDN servers
dwErrCode = LoadCdnsFile(hs);
// Copy the name of the build file
hs->szMainFile = CascNewStr(szMainFile);
// Construct the root path from the name of the build file
CASC_PATH<TCHAR> RootPath(szMainFile, NULL);
hs->szRootPath = RootPath.New(true);
// If either of the root path or build file is known, it's an error
if(hs->szRootPath == NULL || hs->szMainFile == NULL)
{
dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
}
// Now, load the main storage file ".build.info" (or ".build.db" in old storages)
// Initialize variables for local CASC storages
if(dwErrCode == ERROR_SUCCESS)
{
dwErrCode = LoadBuildInfo(hs);
// For local (game) storages, we need the data and indices subdirectory
if(hs->dwFeatures & CASC_FEATURE_DATA_ARCHIVES)
{
if(CheckArchiveFilesDirectories(hs) != ERROR_SUCCESS)
hs->dwFeatures &= ~CASC_FEATURE_DATA_ARCHIVES;
}
// For data files storage, we need that folder
if(hs->dwFeatures & CASC_FEATURE_DATA_FILES)
{
if(CheckDataFilesDirectory(hs) != ERROR_SUCCESS)
hs->dwFeatures &= ~CASC_FEATURE_DATA_FILES;
}
// Enable caching of the sockets. This will add references
// to all existing and all future sockets
if(hs->dwFeatures & CASC_FEATURE_ONLINE)
sockets_set_caching(true);
// Now, load the main storage file (".build.info", ".build.db" or "versions")
dwErrCode = LoadMainFile(hs);
}
// Proceed with loading the CDN config file
@@ -1439,33 +1379,49 @@ bool WINAPI CascOpenStorageEx(LPCTSTR szParams, PCASC_OPEN_STORAGE_ARGS pArgs, b
}
}
// Allocate the storage structure
// Now we need to get the CASC main file, which is either
// [*] .build.info - for current local storages
// [*] .build.db - for older local storages
// [*] versions - for cached online storages
// If there is none of these and `bOnlineStorage` is specified,
// CascLib will download it, as long as the product code was specified
if(dwErrCode == ERROR_SUCCESS)
{
if((hs = new TCascStorage()) != NULL)
{
// Setup the directories
dwErrCode = (bOnlineStorage) ? InitializeOnlineDirectories(hs, pArgs) : InitializeLocalDirectories(hs, pArgs);
if(dwErrCode == ERROR_SUCCESS)
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)
{
// Perform the entire storage loading
dwErrCode = LoadCascStorage(hs, pArgs);
dwErrCode = LoadCascStorage(hs, pArgs, BuildFile.szFullPath, BuildFile.BuildFileType, dwFeatures | 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);
}
// If the caller requested an online storage, we must have the code name
else if((dwErrCode = CheckOnlineStorage(pArgs, BuildFile, dwFeatures)) == ERROR_SUCCESS)
{
dwErrCode = LoadCascStorage(hs, pArgs, BuildFile.szFullPath, BuildFile.BuildFileType, dwFeatures | CASC_FEATURE_DATA_FILES);
}
}
}
// Handle errors
// Delete the storage on error
if(dwErrCode != ERROR_SUCCESS)
{
SetCascError(dwErrCode);
hs = hs->Release();
}
// Free the copy of the parameters
CASC_FREE(szParamsCopy);
// Give the output parameter to the caller
*phStorage = (HANDLE)hs;
if(phStorage != NULL)
phStorage[0] = (HANDLE)hs;
if(dwErrCode != ERROR_SUCCESS)
SetCascError(dwErrCode);
return (dwErrCode == ERROR_SUCCESS);
}