aboutsummaryrefslogtreecommitdiff
path: root/dep/CascLib/src/CascOpenStorage.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'dep/CascLib/src/CascOpenStorage.cpp')
-rw-r--r--dep/CascLib/src/CascOpenStorage.cpp445
1 files changed, 281 insertions, 164 deletions
diff --git a/dep/CascLib/src/CascOpenStorage.cpp b/dep/CascLib/src/CascOpenStorage.cpp
index 53031169cbb..e10db730d41 100644
--- a/dep/CascLib/src/CascOpenStorage.cpp
+++ b/dep/CascLib/src/CascOpenStorage.cpp
@@ -21,12 +21,6 @@
#define CASC_MAX_ORPHANED_ITEMS 0x100
//-----------------------------------------------------------------------------
-// Local variables
-
-static PFNPROGRESSCALLBACK PfnProgressCallback = NULL;
-static void * PtrProgressParam = NULL;
-
-//-----------------------------------------------------------------------------
// TCascStorage service functions
TCascStorage::TCascStorage()
@@ -50,12 +44,6 @@ TCascStorage::TCascStorage()
BuildFileType = CascBuildNone;
LocalFiles = TotalFiles = EKeyEntries = OrphanItems = SkippedItems = EKeyLength = FileOffsetBits = 0;
-
- // Take the callback param and data. Zero the global pointers
- PfnCallback = PfnProgressCallback;
- PtrCallbackParam = PtrProgressParam;
- PfnProgressCallback = NULL;
- PtrProgressParam = NULL;
}
TCascStorage::~TCascStorage()
@@ -414,7 +402,7 @@ static int LoadEncodingManifest(TCascStorage * hs)
int nError = ERROR_SUCCESS;
// Inform the user about what we are doing
- if(hs->PfnCallback && hs->PfnCallback(hs->PtrCallbackParam, "Loading ENCODING manifest", NULL, 0, 0))
+ if(InvokeProgressCallback(hs, "Loading ENCODING manifest", NULL, 0, 0))
return ERROR_CANCELLED;
// Load the entire encoding file to memory
@@ -745,7 +733,7 @@ static int LoadDownloadManifest(TCascStorage * hs)
int nError = ERROR_SUCCESS;
// Inform the user about what we are doing
- if(hs->PfnCallback && hs->PfnCallback(hs->PtrCallbackParam, "Loading DOWNLOAD manifest", NULL, 0, 0))
+ if(InvokeProgressCallback(hs, "Loading DOWNLOAD manifest", NULL, 0, 0))
return ERROR_CANCELLED;
// Load the entire DOWNLOAD file to memory
@@ -782,7 +770,7 @@ static int LoadInstallManifest(TCascStorage * hs)
int nError = ERROR_SUCCESS;
// Inform the user about what we are doing
- if(hs->PfnCallback && hs->PfnCallback(hs->PtrCallbackParam, "Loading INSTALL manifest", NULL, 0, 0))
+ if(InvokeProgressCallback(hs, "Loading INSTALL manifest", NULL, 0, 0))
return ERROR_CANCELLED;
// Load the entire DOWNLOAD file to memory
@@ -843,7 +831,7 @@ static int LoadBuildManifest(TCascStorage * hs, DWORD dwLocaleMask)
pCKeyEntry = FindCKeyEntry_CKey(hs, pCKeyEntry->CKey);
// Inform the user about what we are doing
- if(hs->PfnCallback && hs->PfnCallback(hs->PtrCallbackParam, "Loading ROOT manifest", NULL, 0, 0))
+ if(InvokeProgressCallback(hs, "Loading ROOT manifest", NULL, 0, 0))
return ERROR_CANCELLED;
// Load the entire ROOT file to memory
@@ -904,83 +892,6 @@ static int LoadBuildManifest(TCascStorage * hs, DWORD dwLocaleMask)
return nError;
}
-static int InitializeLocalDirectories(TCascStorage * hs, const TCHAR * szPath)
-{
- TCHAR * szWorkPath;
- int nError = 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(szPath);
- if(szWorkPath != NULL)
- {
- // Get the length and go up until we find the ".build.info" or ".build.db"
- for(;;)
- {
- // Is this a game directory?
- nError = CheckGameDirectory(hs, szWorkPath);
- if(nError == ERROR_SUCCESS)
- {
- nError = ERROR_SUCCESS;
- break;
- }
-
- // Cut one path part
- if(!CutLastPathPart(szWorkPath))
- {
- nError = ERROR_FILE_NOT_FOUND;
- break;
- }
- }
-
- // Find the index directory
- if (nError == ERROR_SUCCESS)
- {
- // First, check for more common "data" subdirectory
- if ((hs->szIndexPath = CheckForIndexDirectory(hs, _T("data"))) != NULL)
- nError = ERROR_SUCCESS;
-
- // Second, try the "darch" subdirectory (older builds of HOTS - Alpha)
- else if ((hs->szIndexPath = CheckForIndexDirectory(hs, _T("darch"))) != NULL)
- nError = ERROR_SUCCESS;
-
- else
- nError = ERROR_FILE_NOT_FOUND;
- }
-
- // Free the work path buffer
- CASC_FREE(szWorkPath);
- }
-
- return nError;
-}
-
-static int InitializeOnlineDirectories(TCascStorage * hs, LPCTSTR szLocalCache, LPCSTR szCodeName, LPCSTR szRegion)
-{
- TCHAR szCodeNameT[0x40];
-
- CascStrCopy(szCodeNameT, _countof(szCodeNameT), szCodeName);
- hs->szRootPath = CombinePath(szLocalCache, szCodeNameT);
- if (hs->szRootPath != NULL)
- {
- // Create the name of the build file
- hs->szBuildFile = CombinePath(hs->szRootPath, _T("versions"));
- if(hs->szBuildFile != NULL)
- {
- hs->szCodeName = CascNewStr(szCodeNameT);
- hs->szRegion = CascNewStr(szRegion);
- if(hs->szCodeName != NULL)
- {
- hs->BuildFileType = CascVersionsDb;
- hs->dwFeatures |= CASC_FEATURE_ONLINE;
- return ERROR_SUCCESS;
- }
- }
- }
-
- return ERROR_NOT_ENOUGH_MEMORY;
-}
-
static DWORD GetStorageTotalFileCount(TCascStorage * hs)
{
PCASC_CKEY_ENTRY pCKeyEntry;
@@ -1076,68 +987,205 @@ static bool GetStorageTags(TCascStorage * hs, void * pvStorageInfo, size_t cbSto
return (pTags != NULL);
}
-static int LoadCascStorage(TCascStorage * hs, DWORD dwLocaleMask)
+static bool GetStoragePathProduct(TCascStorage * hs, void * pvStorageInfo, size_t cbStorageInfo, size_t * pcbLengthNeeded)
{
- int nError = ERROR_SUCCESS;
+ LPTSTR szBuffer = (LPTSTR)pvStorageInfo;
+ size_t nMaxChars = cbStorageInfo / sizeof(TCHAR);
+ size_t nLength;
+
+ // Calculate the length needed
+ nLength = _tcslen(hs->szRootPath);
+ if(hs->szCodeName != NULL)
+ nLength = nLength + 1 + _tcslen(hs->szCodeName);
+ if(hs->szRegion != NULL)
+ nLength = nLength + 1 + strlen(hs->szRegion);
+ nLength++;
+
+ // Verify whether we have enough space in the buffer
+ szBuffer = (LPTSTR)ProbeOutputBuffer(pvStorageInfo, cbStorageInfo, (nLength * sizeof(TCHAR)), pcbLengthNeeded);
+ if(szBuffer != NULL)
+ {
+ LPTSTR szBufferEnd = szBuffer + nMaxChars;
+
+ // Copy the storage path
+ CascStrCopy(szBuffer, (szBufferEnd - szBuffer), hs->szRootPath);
+ szBuffer += _tcslen(hs->szRootPath);
+
+ // Append the product code name, if any
+ if(hs->szCodeName != NULL)
+ {
+ *szBuffer++ = _T(':');
+ CascStrCopy(szBuffer, (szBufferEnd - szBuffer), hs->szCodeName);
+ szBuffer += _tcslen(hs->szCodeName);
+ }
+
+ // Append the product region, if any
+ if(hs->szRegion != NULL)
+ {
+ *szBuffer++ = _T(':');
+ CascStrCopy(szBuffer, (szBufferEnd - szBuffer), hs->szRegion);
+ }
+ }
+
+ return (szBuffer != NULL);
+}
+
+static DWORD InitializeLocalDirectories(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs)
+{
+ TCHAR * 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)
+{
+ LPCTSTR szLocalCache = pArgs->szLocalPath;
+ LPCTSTR szCodeName = pArgs->szCodeName;
+
+ // Create the root path
+ hs->szRootPath = CombinePath(szLocalCache, szCodeName);
+ if (hs->szRootPath != NULL)
+ {
+ // Create the name of the build file
+ hs->szBuildFile = CombinePath(hs->szRootPath, _T("versions"));
+ if(hs->szBuildFile != NULL)
+ {
+ hs->BuildFileType = CascVersionsDb;
+ hs->dwFeatures |= CASC_FEATURE_ONLINE;
+ return ERROR_SUCCESS;
+ }
+ }
+
+ return ERROR_NOT_ENOUGH_MEMORY;
+}
+
+static DWORD LoadCascStorage(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs)
+{
+ LPCTSTR szCodeName = NULL;
+ LPCTSTR szRegion = NULL;
+ char szRegionA[0x40];
+ DWORD dwLocaleMask = 0;
+ DWORD dwErrCode = ERROR_SUCCESS;
+
+ // Pass the argument array to the storage
+ hs->pArgs = pArgs;
+
+ // Extract optional arguments
+ ExtractVersionedArgument(pArgs, offsetof(CASC_OPEN_STORAGE_ARGS, dwLocaleMask), &dwLocaleMask);
+
+ // Extract the product code name
+ if(ExtractVersionedArgument(pArgs, offsetof(CASC_OPEN_STORAGE_ARGS, szCodeName), &szCodeName) && szCodeName != NULL)
+ hs->szCodeName = CascNewStr(szCodeName);
+
+ // Extract the region (optional)
+ if(ExtractVersionedArgument(pArgs, offsetof(CASC_OPEN_STORAGE_ARGS, szRegion), &szRegion) && szRegion != NULL)
+ {
+ CascStrCopy(szRegionA, _countof(szRegionA), szRegion);
+ hs->szRegion = CascNewStr(szRegionA);
+ }
// For online storages, we need to load CDN servers
- if ((nError == ERROR_SUCCESS) && (hs->dwFeatures & CASC_FEATURE_ONLINE))
+ if ((dwErrCode == ERROR_SUCCESS) && (hs->dwFeatures & CASC_FEATURE_ONLINE))
{
- nError = LoadCdnsInfo(hs);
+ dwErrCode = LoadCdnsInfo(hs);
}
// Now, load the main storage file ".build.info" (or ".build.db" in old storages)
- if(nError == ERROR_SUCCESS)
+ if(dwErrCode == ERROR_SUCCESS)
{
- nError = LoadBuildInfo(hs);
+ dwErrCode = LoadBuildInfo(hs);
}
// If the .build.info OR .build.db file has been loaded,
// proceed with loading the CDN config file
- if (nError == ERROR_SUCCESS)
+ if (dwErrCode == ERROR_SUCCESS)
{
- nError = LoadCdnConfigFile(hs);
+ dwErrCode = LoadCdnConfigFile(hs);
}
// Proceed with loading the CDN build file
- if (nError == ERROR_SUCCESS)
+ if (dwErrCode == ERROR_SUCCESS)
{
- nError = LoadCdnBuildFile(hs);
+ dwErrCode = LoadCdnBuildFile(hs);
}
// Load the index files. Store information from the index files to the CKeyArray.
- if(nError == ERROR_SUCCESS)
+ if(dwErrCode == ERROR_SUCCESS)
{
- nError = LoadIndexFiles(hs);
+ dwErrCode = LoadIndexFiles(hs);
}
// Load the ENCODING manifest
- if(nError == ERROR_SUCCESS)
+ if(dwErrCode == ERROR_SUCCESS)
{
- nError = LoadEncodingManifest(hs);
+ dwErrCode = LoadEncodingManifest(hs);
}
// We need to load the DOWNLOAD manifest. This will give us the information about
// how many physical files are in the storage, so we can start building file tables
- if(nError == ERROR_SUCCESS)
+ if(dwErrCode == ERROR_SUCCESS)
{
- nError = LoadDownloadManifest(hs);
+ dwErrCode = LoadDownloadManifest(hs);
}
// Load the build manifest ("ROOT" file)
- if(nError == ERROR_SUCCESS)
+ if(dwErrCode == ERROR_SUCCESS)
{
// If we fail to load the ROOT file, we take the file names from the INSTALL manifest
- nError = LoadBuildManifest(hs, dwLocaleMask);
- if (nError != ERROR_SUCCESS)
+ dwErrCode = LoadBuildManifest(hs, dwLocaleMask);
+ if (dwErrCode != ERROR_SUCCESS)
{
- nError = LoadInstallManifest(hs);
+ dwErrCode = LoadInstallManifest(hs);
}
}
// Insert entries for files with well-known names. Their CKeys are in the BUILD file
// See https://wowdev.wiki/TACT#Encoding_table for their list
- if (nError == ERROR_SUCCESS)
+ if (dwErrCode == ERROR_SUCCESS)
{
InsertWellKnownFile(hs, "ENCODING", hs->EncodingCKey);
InsertWellKnownFile(hs, "DOWNLOAD", hs->DownloadCKey);
@@ -1151,85 +1199,151 @@ static int LoadCascStorage(TCascStorage * hs, DWORD dwLocaleMask)
}
// Load the encryption keys
- if (nError == ERROR_SUCCESS)
+ if (dwErrCode == ERROR_SUCCESS)
{
- nError = CascLoadEncryptionKeys(hs);
+ dwErrCode = CascLoadEncryptionKeys(hs);
}
- return nError;
+ // Clear the arg structure
+ hs->pArgs = pArgs;
+ return dwErrCode;
}
-//-----------------------------------------------------------------------------
-// Public functions
-
-void WINAPI CascSetProgressCallback(PFNPROGRESSCALLBACK PtrUserCallback, void * PtrUserParam)
+static LPTSTR ParseOpenParams(LPCTSTR szParams, PCASC_OPEN_STORAGE_ARGS pArgs)
{
- PfnProgressCallback = PtrUserCallback;
- PtrProgressParam = PtrUserParam;
-}
+ LPTSTR szParamsCopy;
-bool WINAPI CascOpenStorage(LPCTSTR szPath, DWORD dwLocaleMask, HANDLE * phStorage)
-{
- TCascStorage * hs;
- int nError = ERROR_NOT_ENOUGH_MEMORY;
+ // The 'szParams' must not be empty
+ if(szParams == NULL || pArgs == NULL || szParams[0] == 0)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return NULL;
+ }
- // Allocate the storage structure
- if((hs = new TCascStorage()) != NULL)
+ // The 'pArgs' must be valid but must not contain 'szLocalPath', 'szCodeName' or 'szRegion'
+ if(pArgs->szLocalPath != NULL || pArgs->szCodeName != NULL || pArgs->szRegion != NULL)
{
- // Setup the directories
- nError = InitializeLocalDirectories(hs, szPath);
- if(nError == ERROR_SUCCESS)
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return NULL;
+ }
+
+ // Make a copy of the parameters so we can temper with them
+ if((szParamsCopy = CascNewStr(szParams)) != NULL)
+ {
+ LPTSTR szPlainName = (LPTSTR)GetPlainFileName(szParamsCopy);
+ LPTSTR szSeparator;
+
+ // The local path is always set
+ pArgs->szLocalPath = szParamsCopy;
+ pArgs->szCodeName = NULL;
+ pArgs->szRegion = NULL;
+
+ // Find the first ":". This will indicate the end of local path and also begin of product code
+ if((szSeparator = _tcschr(szPlainName, _T(':'))) != NULL)
{
- nError = LoadCascStorage(hs, dwLocaleMask);
- if(nError == ERROR_SUCCESS)
+ // The found string is a product code name
+ pArgs->szCodeName = szSeparator + 1;
+ szSeparator[0] = 0;
+
+ // Try again. If found, it is a product region
+ if((szSeparator = _tcschr(szSeparator + 1, _T(':'))) != NULL)
{
- *phStorage = (HANDLE)hs;
- return true;
+ pArgs->szRegion = szSeparator + 1;
+ szSeparator[0] = 0;
}
}
-
- // Delete the so-far-allocated storage
- hs = hs->Release();
+ }
+ else
+ {
+ SetLastError(ERROR_NOT_ENOUGH_MEMORY);
}
- // Failed
- SetLastError(nError);
- *phStorage = NULL;
- return false;
+ return szParamsCopy;
}
-// Allows to browse an online CDN storage
-// szLocalCache: Local folder, where the online file will be cached.
-// szCodeName: Product code name, e.g. "agent" for Battle.net Agent. More info: https://wowdev.wiki/TACT#Products
-// szRegion: The region (or subvariant) of the product. Corresponds to the first column of the "versions" file.
-bool WINAPI CascOpenOnlineStorage(LPCTSTR szLocalCache, LPCSTR szCodeName, LPCSTR szRegion, DWORD dwLocaleMask, HANDLE * phStorage)
+//-----------------------------------------------------------------------------
+// Public functions
+
+bool WINAPI CascOpenStorageEx(LPCTSTR szParams, PCASC_OPEN_STORAGE_ARGS pArgs, bool bOnlineStorage, HANDLE * phStorage)
{
+ CASC_OPEN_STORAGE_ARGS LocalArgs = {sizeof(CASC_OPEN_STORAGE_ARGS)};
+ DWORD (*PfnInitDirs)(TCascStorage * hs, PCASC_OPEN_STORAGE_ARGS pArgs);
TCascStorage * hs;
- int nError = ERROR_NOT_ENOUGH_MEMORY;
+ LPTSTR szParamsCopy = NULL;
+ DWORD dwErrCode = ERROR_NOT_ENOUGH_MEMORY;
+
+ // The storage path[+product[+region]] must either be passed in szParams or in pArgs. Not both.
+ // It is allowed to pass NULL as pArgs if the szParams is not NULL
+ if(szParams != NULL)
+ {
+ if(pArgs == NULL)
+ pArgs = &LocalArgs;
+
+ szParamsCopy = ParseOpenParams(szParams, pArgs);
+ if(szParamsCopy == NULL)
+ return false;
+ }
+ else
+ {
+ // The arguments and the local path must be entered
+ if(pArgs == NULL || pArgs->szLocalPath == NULL || pArgs->szLocalPath[0] == 0)
+ {
+ SetLastError(ERROR_INVALID_PARAMETER);
+ return false;
+ }
+ }
// Allocate the storage structure
if((hs = new TCascStorage()) != NULL)
{
// Setup the directories
- nError = InitializeOnlineDirectories(hs, szLocalCache, szCodeName, szRegion);
- if(nError == ERROR_SUCCESS)
+ PfnInitDirs = (bOnlineStorage) ? InitializeOnlineDirectories : InitializeLocalDirectories;
+ dwErrCode = PfnInitDirs(hs, pArgs);
+ if(dwErrCode == ERROR_SUCCESS)
{
- nError = LoadCascStorage(hs, dwLocaleMask);
- if(nError == ERROR_SUCCESS)
- {
- *phStorage = (HANDLE)hs;
- return true;
- }
+ // Perform the entire storage loading
+ dwErrCode = LoadCascStorage(hs, pArgs);
}
- // Delete the so-far-allocated storage
- hs = hs->Release();
+ // Free the storage structure on fail
+ if(dwErrCode != ERROR_SUCCESS)
+ {
+ hs = hs->Release();
+ }
}
- // Failed
- SetLastError(nError);
- *phStorage = NULL;
- return false;
+ // Give the output parameter to the caller
+ CASC_FREE(szParamsCopy);
+ *phStorage = (HANDLE)hs;
+
+ // Return the result
+ if(dwErrCode != ERROR_SUCCESS)
+ SetLastError(dwErrCode);
+ return (dwErrCode == ERROR_SUCCESS);
+}
+
+// szParams: "LocalPath:CodeName", e.g. "C:\\Games\\World of Warcraft:wowt"
+// * LocalPath: Local folder, where the online file will be cached.
+// * CodeName: Product code name, e.g. "agent" for Battle.net Agent. More info: https://wowdev.wiki/TACT#Products
+bool WINAPI CascOpenStorage(LPCTSTR szParams, DWORD dwLocaleMask, HANDLE * phStorage)
+{
+ CASC_OPEN_STORAGE_ARGS OpenArgs = {sizeof(CASC_OPEN_STORAGE_ARGS)};
+
+ OpenArgs.dwLocaleMask = dwLocaleMask;
+ return CascOpenStorageEx(szParams, &OpenArgs, false, phStorage);
+}
+
+// Allows to browse an online CDN storage
+// szParams: "CachePath:CodeName:Region", e.g. "C:\\Cache:wowt:us"
+// * CachePath: Local folder, where the online file will be cached.
+// * CodeName: Product code name, e.g. "agent" for Battle.net Agent. More info: https://wowdev.wiki/TACT#Products
+// * Region: The region (or subvariant) of the product. Corresponds to the first column of the "versions" file.
+bool WINAPI CascOpenOnlineStorage(LPCTSTR szParams, DWORD dwLocaleMask, HANDLE * phStorage)
+{
+ CASC_OPEN_STORAGE_ARGS OpenArgs = {sizeof(CASC_OPEN_STORAGE_ARGS)};
+
+ OpenArgs.dwLocaleMask = dwLocaleMask;
+ return CascOpenStorageEx(szParams, &OpenArgs, true, phStorage);
}
bool WINAPI CascGetStorageInfo(
@@ -1278,6 +1392,9 @@ bool WINAPI CascGetStorageInfo(
case CascStorageTags:
return GetStorageTags(hs, pvStorageInfo, cbStorageInfo, pcbLengthNeeded);
+ case CascStoragePathProduct:
+ return GetStoragePathProduct(hs, pvStorageInfo, cbStorageInfo, pcbLengthNeeded);
+
default:
SetLastError(ERROR_INVALID_PARAMETER);
return false;