static int FetchAndVerifyConfigFile(TCascStorage * hs, PQUERY_KEY pFileKey, PQUERY_KEY pFileBlob) { TCHAR * szFileName; int nError; // Construct the local file name szFileName = NewStr(hs->szDataPath, 8 + 3 + 3 + 32); if(szFileName == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Add the part where the config file path is AppendConfigFilePath(szFileName, pFileKey); // Load the config file nError = LoadTextFile(szFileName, pFileBlob); if(nError == ERROR_SUCCESS) { // Verify the blob's MD5 if(!VerifyDataBlockHash(pFileBlob->pbData, pFileBlob->cbData, pFileKey->pbData)) { FreeCascBlob(pFileBlob); nError = ERROR_BAD_FORMAT; } } CASC_FREE(szFileName); return nError; }
// Checks whether there is a ".agent.db". If yes, the function // sets "szRootPath" and "szDataPath" in the storage structure // and returns ERROR_SUCCESS int CheckGameDirectory(TCascStorage * hs, TCHAR * szDirectory) { QUERY_KEY AgentFile; TCHAR * szFilePath; size_t nLength = 0; char * szValue; int nError = ERROR_FILE_NOT_FOUND; // Create the full name of the .agent.db file szFilePath = CombinePath(szDirectory, _T(".agent.db")); if(szFilePath != NULL) { // Load the file to memory nError = LoadTextFile(szFilePath, &AgentFile); if(nError == ERROR_SUCCESS) { // Extract the data directory from the ".agent.db" file szValue = ExtractStringVariable(&AgentFile, "data_dir", &nLength); if(szValue != NULL) { hs->szRootPath = CascNewStr(szDirectory, 0); hs->szDataPath = CombinePathAndString(szDirectory, szValue, nLength); nError = (hs->szDataPath != NULL) ? ERROR_SUCCESS : ERROR_NOT_ENOUGH_MEMORY; } // Free the loaded blob FreeCascBlob(&AgentFile); } // Freee the file path CASC_FREE(szFilePath); } return nError; }
static int ParseFile_BuildDb(TCascStorage * hs, void * pvListFile) { QUERY_KEY TagString = {NULL, 0}; CASC_CSV Csv; int nError; // Load the single line from the text file if(Csv.LoadNextLine(pvListFile) == 0) return ERROR_BAD_FORMAT; // Extract the CDN build key nError = Csv.GetData(hs->CdnBuildKey, 0, true); if (nError != ERROR_SUCCESS) return nError; // Load the CDN config key nError = Csv.GetData(hs->CdnConfigKey, 1, true); if (nError != ERROR_SUCCESS) return nError; // Load the the tags nError = Csv.GetData(TagString, 2, false); if (nError == ERROR_SUCCESS && TagString.pbData != NULL) { GetDefaultLocaleMask(hs, &TagString); FreeCascBlob(&TagString); } // Load the URL hs->szCdnList = Csv.GetString(3); if (hs->szCdnList == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Verify all variables if (hs->CdnBuildKey.pbData == NULL || hs->CdnConfigKey.pbData == NULL) nError = ERROR_BAD_FORMAT; return nError; }
int LoadBuildInfo(TCascStorage * hs) { QUERY_KEY InfoFile = {NULL, 0}; QUERY_KEY FileData = {NULL, 0}; TCHAR * szAgentFile; TCHAR * szInfoFile; bool bBuildConfigComplete = false; int nError = ERROR_SUCCESS; // Since HOTS build 30027, the game uses build.info file for storage info if(bBuildConfigComplete == false) { szInfoFile = CombinePath(hs->szRootPath, _T(".build.info")); if(szInfoFile != NULL) { nError = LoadTextFile(szInfoFile, &InfoFile); if(nError == ERROR_SUCCESS) { // Parse the info file nError = ParseInfoFile(hs, &InfoFile); if(nError == ERROR_SUCCESS) bBuildConfigComplete = true; // Free the loaded blob FreeCascBlob(&InfoFile); } CASC_FREE(szInfoFile); } } // If the info file has not been loaded, try the legacy .build.db if(bBuildConfigComplete == false) { szAgentFile = CombinePath(hs->szRootPath, _T(".build.db")); if(szAgentFile != NULL) { nError = LoadTextFile(szAgentFile, &FileData); if(nError == ERROR_SUCCESS) { nError = ParseAgentFile(hs, &FileData); if(nError == ERROR_SUCCESS) bBuildConfigComplete = true; FreeCascBlob(&FileData); } CASC_FREE(szAgentFile); } } // If the .build.info and .build.db file hasn't been loaded, if(nError == ERROR_SUCCESS && bBuildConfigComplete == false) { nError = ERROR_FILE_CORRUPT; } // Load the configuration file if(nError == ERROR_SUCCESS) { nError = FetchAndVerifyConfigFile(hs, &hs->CdnConfigKey, &FileData); if(nError == ERROR_SUCCESS) { nError = LoadCdnConfigFile(hs, &FileData); FreeCascBlob(&FileData); } } // Load the build file if(nError == ERROR_SUCCESS) { nError = FetchAndVerifyConfigFile(hs, &hs->CdnBuildKey, &FileData); if(nError == ERROR_SUCCESS) { nError = LoadCdnBuildFile(hs, &FileData); FreeCascBlob(&FileData); } } // Fill the index directory if(nError == ERROR_SUCCESS) { // First, check for more common "data" subdirectory if((hs->szIndexPath = CheckForIndexDirectory(hs, _T("data"))) != NULL) return ERROR_SUCCESS; // Second, try the "darch" subdirectory (older builds of HOTS - Alpha) if((hs->szIndexPath = CheckForIndexDirectory(hs, _T("darch"))) != NULL) return ERROR_SUCCESS; nError = ERROR_FILE_NOT_FOUND; } return nError; }
static int ParseInfoFile(TCascStorage * hs, PQUERY_KEY pFileBlob) { QUERY_KEY Active = {NULL, 0}; QUERY_KEY TagString = {NULL, 0}; QUERY_KEY CdnHost = {NULL, 0}; QUERY_KEY CdnPath = {NULL, 0}; const char * szLineBegin1 = NULL; const char * szLinePtr1 = NULL; const char * szLineBegin2 = NULL; const char * szLineEnd1 = NULL; const char * szLineEnd2 = NULL; const char * szFileEnd = (const char *)(pFileBlob->pbData + pFileBlob->cbData); const char * szFilePtr = (const char *)pFileBlob->pbData; int nError = ERROR_BAD_FORMAT; // Find the first line szLineBegin1 = szFilePtr; while(szFilePtr < szFileEnd) { // Check for the end of the line if(szFilePtr[0] == 0x0D || szFilePtr[0] == 0x0A) { szLineEnd1 = szFilePtr; break; } szFilePtr++; } while (szFilePtr < szFileEnd) { // Rewind header line pointers szLinePtr1 = szLineBegin1; // Skip the newline character(s) while (szFilePtr < szFileEnd && (szFilePtr[0] == 0x0D || szFilePtr[0] == 0x0A)) szFilePtr++; // Find the next line szLineBegin2 = szFilePtr; while (szFilePtr < szFileEnd) { // Check for the end of the line if (szFilePtr[0] == 0x0D || szFilePtr[0] == 0x0A) { szLineEnd2 = szFilePtr; break; } szFilePtr++; } // Find the build key, CDN config key and the URL path while (szLinePtr1 < szLineEnd1) { // Check for variables we need if (IsInfoVariable(szLinePtr1, szLineEnd1, "Active", "DEC")) LoadInfoVariable(&Active, szLineBegin2, szLineEnd2, false); if (IsInfoVariable(szLinePtr1, szLineEnd1, "Build Key", "HEX")) LoadInfoVariable(&hs->CdnBuildKey, szLineBegin2, szLineEnd2, true); if (IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Key", "HEX")) LoadInfoVariable(&hs->CdnConfigKey, szLineBegin2, szLineEnd2, true); if (IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Hosts", "STRING")) LoadInfoVariable(&CdnHost, szLineBegin2, szLineEnd2, false); if (IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Path", "STRING")) LoadInfoVariable(&CdnPath, szLineBegin2, szLineEnd2, false); if (IsInfoVariable(szLinePtr1, szLineEnd1, "Tags", "STRING")) LoadInfoVariable(&TagString, szLineBegin2, szLineEnd2, false); // Move both line pointers szLinePtr1 = SkipInfoVariable(szLinePtr1, szLineEnd1); if (szLineBegin1 == NULL) break; szLineBegin2 = SkipInfoVariable(szLineBegin2, szLineEnd2); if (szLineBegin2 == NULL) break; } // Stop parsing if found active config if(Active.pbData != NULL && *Active.pbData == '1') break; } // All four must be present if(hs->CdnBuildKey.pbData != NULL && hs->CdnConfigKey.pbData != NULL && CdnHost.pbData != NULL && CdnPath.pbData != NULL) { // Merge the CDN host and CDN path hs->szUrlPath = CASC_ALLOC(TCHAR, CdnHost.cbData + CdnPath.cbData + 1); if(hs->szUrlPath != NULL) { CopyString(hs->szUrlPath, (char *)CdnHost.pbData, CdnHost.cbData); CopyString(hs->szUrlPath + CdnHost.cbData, (char *)CdnPath.pbData, CdnPath.cbData); nError = ERROR_SUCCESS; } } // If we found tags, we can extract language build from it if(TagString.pbData != NULL) GetDefaultLocaleMask(hs, &TagString); FreeCascBlob(&CdnHost); FreeCascBlob(&CdnPath); FreeCascBlob(&TagString); return nError; }
static int ParseInfoFile(TCascStorage * hs, void * pvListFile) { QUERY_KEY Active = {NULL, 0}; QUERY_KEY TagString = {NULL, 0}; QUERY_KEY CdnHost = {NULL, 0}; QUERY_KEY CdnPath = {NULL, 0}; char szOneLine1[0x200]; char szOneLine2[0x200]; size_t nLength1; size_t nLength2; int nError = ERROR_BAD_FORMAT; // Extract the first line, cotaining the headers nLength1 = ListFile_GetNextLine(pvListFile, szOneLine1, _maxchars(szOneLine1)); if(nLength1 == 0) return ERROR_BAD_FORMAT; // Now parse the second and the next lines. We are looking for line // with "Active" set to 1 for(;;) { const char * szLinePtr1 = szOneLine1; const char * szLineEnd1 = szOneLine1 + nLength1; const char * szLinePtr2 = szOneLine2; const char * szLineEnd2; // Read the next line nLength2 = ListFile_GetNextLine(pvListFile, szOneLine2, _maxchars(szOneLine2)); if(nLength2 == 0) break; szLineEnd2 = szLinePtr2 + nLength2; // Parse all variables while(szLinePtr1 < szLineEnd1) { // Check for variables we need if(IsInfoVariable(szLinePtr1, szLineEnd1, "Active", "DEC")) LoadInfoVariable(&Active, szLinePtr2, szLineEnd2, false); if(IsInfoVariable(szLinePtr1, szLineEnd1, "Build Key", "HEX")) LoadInfoVariable(&hs->CdnBuildKey, szLinePtr2, szLineEnd2, true); if(IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Key", "HEX")) LoadInfoVariable(&hs->CdnConfigKey, szLinePtr2, szLineEnd2, true); if(IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Hosts", "STRING")) LoadInfoVariable(&CdnHost, szLinePtr2, szLineEnd2, false); if(IsInfoVariable(szLinePtr1, szLineEnd1, "CDN Path", "STRING")) LoadInfoVariable(&CdnPath, szLinePtr2, szLineEnd2, false); if(IsInfoVariable(szLinePtr1, szLineEnd1, "Tags", "STRING")) LoadInfoVariable(&TagString, szLinePtr2, szLineEnd2, false); // Move both line pointers szLinePtr1 = SkipInfoVariable(szLinePtr1, szLineEnd1); if(szLinePtr1 == NULL) break; szLinePtr2 = SkipInfoVariable(szLinePtr2, szLineEnd2); if(szLinePtr2 == NULL) break; } // Stop parsing if found active config if(Active.pbData != NULL && *Active.pbData == '1') break; // Free the blobs FreeCascBlob(&CdnHost); FreeCascBlob(&CdnPath); FreeCascBlob(&TagString); } // All four must be present if(hs->CdnBuildKey.pbData != NULL && hs->CdnConfigKey.pbData != NULL && CdnHost.pbData != NULL && CdnPath.pbData != NULL) { // Merge the CDN host and CDN path hs->szUrlPath = CASC_ALLOC(TCHAR, CdnHost.cbData + CdnPath.cbData + 1); if(hs->szUrlPath != NULL) { CopyString(hs->szUrlPath, (char *)CdnHost.pbData, CdnHost.cbData); CopyString(hs->szUrlPath + CdnHost.cbData, (char *)CdnPath.pbData, CdnPath.cbData); nError = ERROR_SUCCESS; } } // If we found tags, we can extract language build from it if(TagString.pbData != NULL) GetDefaultLocaleMask(hs, &TagString); FreeCascBlob(&CdnHost); FreeCascBlob(&CdnPath); FreeCascBlob(&TagString); FreeCascBlob(&Active); return nError; }
static int ParseFile_BuildInfo(TCascStorage * hs, void * pvListFile) { CASC_CSV Csv; size_t Indices[4]; char szActive[4]; int nError; // Extract the first line, containing the headers if (Csv.LoadHeader(pvListFile) == 0) return ERROR_BAD_FORMAT; // Retrieve the column indices if (!Csv.GetColumnIndices(Indices, "Active!DEC:1", "Build Key!HEX:16", "CDN Key!HEX:16", NULL)) return ERROR_BAD_FORMAT; // Find the active config for (;;) { // Load a next line if (Csv.LoadNextLine(pvListFile) == 0) break; // Is that build config active? if (Csv.GetString(szActive, 4, Indices[0]) == ERROR_SUCCESS && !strcmp(szActive, "1")) { // Retrieve the build key nError = Csv.GetData(hs->CdnBuildKey, Indices[1], true); if (nError != ERROR_SUCCESS) return nError; // Retrieve the config key nError = Csv.GetData(hs->CdnConfigKey, Indices[2], true); if (nError != ERROR_SUCCESS) return nError; // "CDN Servers"? if (Csv.GetColumnIndices(Indices, "CDN Servers!STRING:0", "CDN Path!STRING:0", NULL)) { hs->szCdnList = MakeCdnList(Csv, Indices); } // "CDN Hosts"? else if (Csv.GetColumnIndices(Indices, "CDN Hosts!STRING:0", "CDN Path!STRING:0", NULL)) { hs->szCdnList = MakeCdnList(Csv, Indices); } // If we found tags, we can extract language build from it if (Csv.GetColumnIndices(Indices, "Tags!STRING:0", NULL)) { QUERY_KEY TagString = { NULL, 0 }; if (Csv.GetData(TagString, Indices[0], false) == ERROR_SUCCESS && TagString.cbData != 0) { GetDefaultLocaleMask(hs, &TagString); FreeCascBlob(&TagString); } } // Build and Config keys are the ones we do really need return (hs->CdnConfigKey.cbData == MD5_HASH_SIZE && hs->CdnBuildKey.cbData == MD5_HASH_SIZE) ? ERROR_SUCCESS : ERROR_BAD_FORMAT; } } return ERROR_FILE_NOT_FOUND; }