size_t ListFile_GetNext(void * pvListFile, const char * szMask, char * szBuffer, size_t nMaxChars) { size_t nLength = 0; int nError = ERROR_SUCCESS; // Check for parameters for(;;) { // Read the (next) line nLength = ListFile_GetNextLine(pvListFile, szBuffer, nMaxChars); if(nLength == 0) { nError = ERROR_NO_MORE_FILES; break; } // If some mask entered, check it if(CheckWildCard(szBuffer, szMask)) { nError = ERROR_SUCCESS; break; } } if(nError != ERROR_SUCCESS) SetLastError(nError); return nLength; }
static int LoadCdnConfigFile(TCascStorage * hs, void * pvListFile) { const char * szLineBegin; const char * szVarBegin; const char * szLineEnd; int nError = ERROR_SUCCESS; // Keep parsing the listfile while there is something in there for(;;) { // Get the next line if(!ListFile_GetNextLine(pvListFile, &szLineBegin, &szLineEnd)) break; // Archive group szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "archive-group"); if(szVarBegin != NULL) { nError = LoadSingleBlob(&hs->ArchivesGroup, szVarBegin, szLineEnd); continue; } // Archives szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "archives"); if(szVarBegin != NULL) { nError = LoadMultipleBlobs(&hs->ArchivesKey, szVarBegin, szLineEnd); continue; } // Patch archive group szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "patch-archive-group"); if(szVarBegin != NULL) { LoadSingleBlob(&hs->PatchArchivesKey, szVarBegin, szLineEnd); continue; } // Patch archives szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "patch-archives"); if(szVarBegin != NULL) { nError = LoadMultipleBlobs(&hs->PatchArchivesKey, szVarBegin, szLineEnd); continue; } } // Check if all required fields are present if(hs->ArchivesKey.pbData == NULL || hs->ArchivesKey.cbData == 0) return ERROR_BAD_FORMAT; return nError; }
static int ParseAgentFile(TCascStorage * hs, void * pvListFile) { const char * szLinePtr; const char * szLineEnd; char szOneLine[0x200]; size_t nLength; int nError; // Load the single line from the text file nLength = ListFile_GetNextLine(pvListFile, szOneLine, _maxchars(szOneLine)); if(nLength == 0) return ERROR_BAD_FORMAT; // Set the line range szLinePtr = szOneLine; szLineEnd = szOneLine + nLength; // Extract the CDN build key nError = LoadInfoVariable(&hs->CdnBuildKey, szLinePtr, szLineEnd, true); if(nError == ERROR_SUCCESS) { // Skip the variable szLinePtr = SkipInfoVariable(szLinePtr, szLineEnd); // Load the CDN config hash nError = LoadInfoVariable(&hs->CdnConfigKey, szLinePtr, szLineEnd, true); if(nError == ERROR_SUCCESS) { // Skip the variable szLinePtr = SkipInfoVariable(szLinePtr, szLineEnd); // Skip the Locale/OS/code variable szLinePtr = SkipInfoVariable(szLinePtr, szLineEnd); // Load the URL hs->szUrlPath = CascNewStrFromAnsi(szLinePtr, szLineEnd); if(hs->szUrlPath == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; } } // Verify all variables if(hs->CdnBuildKey.pbData == NULL || hs->CdnConfigKey.pbData == NULL || hs->szUrlPath == NULL) nError = ERROR_BAD_FORMAT; return nError; }
static int ParseFile_CdnConfig(TCascStorage * hs, void * pvListFile) { const char * szLineBegin; const char * szLineEnd; int nError = ERROR_SUCCESS; // Keep parsing the listfile while there is something in there for(;;) { // Get the next line if(!ListFile_GetNextLine(pvListFile, &szLineBegin, &szLineEnd)) break; // CDN key of ARCHIVE-GROUP. Archive-group is actually a very special '.index' file. // It is essentially a merger of all .index files, with a structure change // When ".index" added after the ARCHIVE-GROUP, we get file name in "indices" folder if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "archive-group", LoadQueryKey, &hs->ArchiveGroup)) continue; // CDN key of all archives. When ".index" added to the string, we get file name in "indices" folder if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "archives", LoadQueryKey, &hs->ArchivesKey)) continue; // CDN keys of patch archives (needs research) if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "patch-archives", LoadQueryKey, &hs->PatchArchivesKey)) continue; // CDN key of probably the combined patch index file (needs research) if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "patch-archive-group", LoadQueryKey, &hs->PatchArchivesGroup)) continue; // List of build configs this config supports (optional) // Points to file: "data\config\%02X\%02X\%s if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "builds", LoadQueryKey, &hs->BuildFiles)) continue; } // Check if all required fields are present if(hs->ArchivesKey.pbData == NULL || hs->ArchivesKey.cbData == 0) return ERROR_BAD_FORMAT; return nError; }
size_t ListFile_GetNextLine(void * pvListFile, char * szBuffer, size_t nMaxChars) { const char * szLineBegin = NULL; const char * szLineEnd = NULL; size_t nLength; // Retrieve the next line nLength = ListFile_GetNextLine(pvListFile, &szLineBegin, &szLineEnd); // Check the length if(nLength > nMaxChars) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return 0; } // Copy the line to the user buffer memcpy(szBuffer, szLineBegin, nLength); szBuffer[nLength] = 0; return nLength; }
static int LoadCdnBuildFile(TCascStorage * hs, void * pvListFile) { const char * szLineBegin; const char * szVarBegin; const char * szLineEnd = NULL; int nError = ERROR_SUCCESS; for(;;) { // Get the next line if(!ListFile_GetNextLine(pvListFile, &szLineBegin, &szLineEnd)) break; // Game name szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "build-product"); if(szVarBegin != NULL) { GetGameType(hs, szVarBegin, szLineEnd); continue; } // Game build number szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "build-name"); if(szVarBegin != NULL) { GetBuildNumber(hs, szVarBegin, szLineEnd); continue; } // Root szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "root"); if(szVarBegin != NULL) { LoadSingleBlob(&hs->RootKey, szVarBegin, szLineEnd); continue; } // Patch szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "patch"); if(szVarBegin != NULL) { LoadSingleBlob(&hs->PatchKey, szVarBegin, szLineEnd); continue; } // Download szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "download"); if(szVarBegin != NULL) { LoadSingleBlob(&hs->DownloadKey, szVarBegin, szLineEnd); continue; } // Install szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "install"); if(szVarBegin != NULL) { LoadSingleBlob(&hs->InstallKey, szVarBegin, szLineEnd); continue; } // Encoding keys szVarBegin = CheckLineVariable(szLineBegin, szLineEnd, "encoding"); if(szVarBegin != NULL) { nError = LoadMultipleBlobs(&hs->EncodingKey, szVarBegin, szLineEnd, 2); continue; } } // Check the encoding keys if(hs->EncodingKey.pbData == NULL || hs->EncodingKey.cbData != MD5_HASH_SIZE * 2) return ERROR_BAD_FORMAT; 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_CdnBuild(TCascStorage * hs, void * pvListFile) { const char * szLineBegin; const char * szLineEnd = NULL; int nError; // Initialize the size of the internal files ConvertIntegerToBytes_4(CASC_INVALID_SIZE, hs->EncodingFile.ContentSize); // Initialize the empty VFS array nError = hs->VfsRootList.Create<QUERY_KEY_PAIR>(0x10); if (nError != ERROR_SUCCESS) return nError; // Parse all variables while(ListFile_GetNextLine(pvListFile, &szLineBegin, &szLineEnd) != 0) { // Product name and build name if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "build-product", LoadBuildProduct, NULL)) continue; if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "build-name", LoadBuildName, NULL)) continue; // Content key of the ROOT file. Look this up in ENCODING to get the encoded key if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "root", LoadCkeyEkey, &hs->RootFile)) continue; // Content key [+ encoded key] of the INSTALL file // If CKey is absent, you need to query the ENCODING file for it if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "install", LoadCkeyEkey, &hs->InstallFile)) continue; // Content key [+ encoded key] of the DOWNLOAD file // If CKey is absent, you need to query the ENCODING file for it if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "download", LoadCkeyEkey, &hs->DownloadFile)) continue; // Content key + encoded key of the ENCODING file. Contains CKey+EKey // If either none or 1 is found, the game (at least Wow) switches to plain-data(?). Seen in build 20173 if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "encoding", LoadCkeyEkey, &hs->EncodingFile)) continue; // Content and encoded size of the ENCODING file. This helps us to determine size // of the ENCODING file better, as the size in the EKEY entries is almost always wrong on WoW storages if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "encoding-size", LoadCkeyEkeySize, &hs->EncodingFile)) continue; // PATCH file if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "patch", LoadCkeyEkey, &hs->PatchFile)) continue; // Load the CKey+EKey of a VFS root file (the root file of the storage VFS) if (CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "vfs-root", LoadCkeyEkey, &hs->VfsRoot)) continue; // Load the content size of the VFS root if (CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "vfs-root-size", LoadCkeyEkeySize, &hs->VfsRoot)) continue; // Load a directory entry to the VFS if(CheckConfigFileVariable(hs, szLineBegin, szLineEnd, "vfs-*", LoadVfsRootEntry, &hs->VfsRootList)) continue; } // Both CKey and EKey of ENCODING file is required if(!IsValidMD5(hs->EncodingFile.CKey) || !IsValidMD5(hs->EncodingFile.EKey)) return ERROR_BAD_FORMAT; return nError; }