static int LoadInfoVariable(PQUERY_KEY pVarBlob, const char * szLineBegin, const char * szLineEnd, bool bHexaValue) { const char * szLinePtr = szLineBegin; // Sanity checks assert(pVarBlob->pbData == NULL); assert(pVarBlob->cbData == 0); // Check length of the variable while(szLinePtr < szLineEnd && szLinePtr[0] != '|') szLinePtr++; // Allocate space for the blob if(bHexaValue) { // Initialize the blob pVarBlob->pbData = CASC_ALLOC(BYTE, (szLinePtr - szLineBegin) / 2); return StringBlobToBinaryBlob(pVarBlob, (LPBYTE)szLineBegin, (LPBYTE)szLinePtr); } // Initialize the blob pVarBlob->pbData = CASC_ALLOC(BYTE, (szLinePtr - szLineBegin) + 1); pVarBlob->cbData = (DWORD)(szLinePtr - szLineBegin); // Check for success if(pVarBlob->pbData == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Copy the string memcpy(pVarBlob->pbData, szLineBegin, pVarBlob->cbData); pVarBlob->pbData[pVarBlob->cbData] = 0; return ERROR_SUCCESS; }
static PQUERY_KEY LoadMultipleBlobs(LPBYTE pbLineBegin, LPBYTE pbLineEnd, DWORD * pdwBlobCount) { PQUERY_KEY pBlobArray = NULL; LPBYTE pbBuffer = NULL; DWORD dwBlobCount = GetBlobCount(pbLineBegin, pbLineEnd); int nError; // Only if there is at least 1 blob if(dwBlobCount != 0) { // Allocate the array of blobs pBlobArray = CASC_ALLOC(QUERY_KEY, dwBlobCount); if(pBlobArray != NULL) { // Zero the blob array memset(pBlobArray, 0, dwBlobCount * sizeof(QUERY_KEY)); // Allocate buffer for the blobs pbBuffer = CASC_ALLOC(BYTE, dwBlobCount * MAX_CASC_KEY_LENGTH); if(pbBuffer != NULL) { // Zero the buffer memset(pbBuffer, 0, dwBlobCount * MAX_CASC_KEY_LENGTH); // Load the entire blob array nError = LoadBlobArray(pBlobArray, dwBlobCount, pbLineBegin, pbLineEnd, pbBuffer, dwBlobCount * MAX_CASC_KEY_LENGTH); if(nError == ERROR_SUCCESS) { *pdwBlobCount = dwBlobCount; return pBlobArray; } // Free the buffer CASC_FREE(pbBuffer); } // Free the array of blobs CASC_FREE(pBlobArray); pBlobArray = NULL; } // Reset the blob count dwBlobCount = 0; } *pdwBlobCount = dwBlobCount; return pBlobArray; }
static TListFileCache * CreateListFileCache(RELOAD_CACHE pfnReloadCache, CLOSE_STREAM pfnCloseStream, void * pvCacheContext, DWORD dwFileSize) { TListFileCache * pCache = NULL; DWORD dwBytesToRead; // Allocate cache for one file block pCache = (TListFileCache *)CASC_ALLOC(BYTE, sizeof(TListFileCache)); if(pCache != NULL) { // Clear the entire structure memset(pCache, 0, sizeof(TListFileCache)); pCache->pfnReloadCache = pfnReloadCache; pCache->pfnCloseStream = pfnCloseStream; pCache->pvCacheContext = pvCacheContext; pCache->dwFileSize = dwFileSize; // Load the file cache from the file dwBytesToRead = CASCLIB_MIN(CACHE_BUFFER_SIZE, dwFileSize); if(pfnReloadCache(pvCacheContext, pCache->Buffer, dwBytesToRead)) { // Allocate pointers pCache->pBegin = pCache->pPos = &pCache->Buffer[0]; pCache->pEnd = pCache->pBegin + dwBytesToRead; } else { ListFile_Free(pCache); pCache = NULL; } } // Return the cache return pCache; }
static LPBYTE LoadFileToMemory(TCascStorage * hs, LPBYTE pbEncodingKey, DWORD * pcbFileData) { QUERY_KEY EncodingKey; LPBYTE pbFileData = NULL; HANDLE hFile; DWORD cbBytesRead = 0; DWORD cbFileData = 0; // Open the file by encoding key EncodingKey.pbData = pbEncodingKey; EncodingKey.cbData = MD5_HASH_SIZE; if(CascOpenFileByEncodingKey((HANDLE)hs, &EncodingKey, 0, &hFile)) { // Retrieve the file size cbFileData = CascGetFileSize(hFile, NULL); if(cbFileData > 0) { pbFileData = CASC_ALLOC(BYTE, cbFileData); if(pbFileData != NULL) { CascReadFile(hFile, pbFileData, cbFileData, &cbBytesRead); } } // Close the file CascCloseFile(hFile); } // Give the file to the caller if(pcbFileData != NULL) pcbFileData[0] = cbBytesRead; return pbFileData; }
static TCascFile * CreateFileHandle(TCascStorage * hs, PCASC_INDEX_ENTRY pIndexEntry) { ULONGLONG FileOffsMask = ((ULONGLONG)1 << hs->KeyMapping[0].SegmentBits) - 1; ULONGLONG FileOffset = ConvertBytesToInteger_5(pIndexEntry->FileOffsetBE); TCascFile * hf; // Allocate the CASC file structure hf = (TCascFile *)CASC_ALLOC(TCascFile, 1); if(hf != NULL) { // Initialize the structure memset(hf, 0, sizeof(TCascFile)); hf->ArchiveIndex = (DWORD)(FileOffset >> hs->KeyMapping[0].SegmentBits); hf->HeaderOffset = (DWORD)(FileOffset & FileOffsMask); hf->szClassName = "TCascFile"; // Copy the file size. Note that for all files except ENCODING, // this is the compressed file size hf->CompressedSize = ConvertBytesToInteger_4_LE(pIndexEntry->FileSizeLE); // For now, we set the file size to be equal to compressed size // This is used when loading the ENCODING file, which does not // have entry in the encoding table hf->FileSize = hf->CompressedSize; // Increment the number of references to the archive hs->dwRefCount++; hf->hs = hs; }
static int LoadFileFrames(TCascFile * hf) { PBLTE_FRAME pFileFrames; PBLTE_FRAME pFileFrame; ULONGLONG ArchiveFileOffset; DWORD FrameOffset = 0; DWORD FileSize = 0; int nError = ERROR_SUCCESS; assert(hf != NULL); assert(hf->pStream != NULL); assert(hf->pFrames != NULL); // Allocate frame array pFileFrames = pFileFrame = CASC_ALLOC(BLTE_FRAME, hf->FrameCount); if(pFileFrames != NULL) { // Load the frame array ArchiveFileOffset = hf->FramesOffset; if(FileStream_Read(hf->pStream, &ArchiveFileOffset, pFileFrames, hf->FrameCount * sizeof(BLTE_FRAME))) { // Move the raw archive offset ArchiveFileOffset += (hf->FrameCount * sizeof(BLTE_FRAME)); // Copy the frames to the file structure for(DWORD i = 0; i < hf->FrameCount; i++, pFileFrame++) { hf->pFrames[i].FrameArchiveOffset = (DWORD)ArchiveFileOffset; hf->pFrames[i].FrameFileOffset = FrameOffset; hf->pFrames[i].CompressedSize = ConvertBytesToInteger_4(pFileFrame->CompressedSize); hf->pFrames[i].FrameSize = ConvertBytesToInteger_4(pFileFrame->FrameSize); memcpy(hf->pFrames[i].md5, pFileFrame->md5, MD5_HASH_SIZE); ArchiveFileOffset += hf->pFrames[i].CompressedSize; FrameOffset += hf->pFrames[i].FrameSize; FileSize += hf->pFrames[i].FrameSize; } } else nError = GetLastError(); // Note: on ENCODING file, this value is almost always bigger // then the real size of ENCODING. We handle this problem // by calculating size of the ENCODIG file from its header. hf->FileSize = FileSize; #ifdef CASCLIB_TEST hf->FileSize_FrameSum = FileSize; #endif // Free the array CASC_FREE(pFileFrames); } else nError = ERROR_NOT_ENOUGH_MEMORY; return nError; }
static TCHAR * MakeCdnList(CASC_CSV & Csv, size_t * Indices) { TCHAR * szCdnList = NULL; TCHAR * szCdnPtr; TCHAR * szCdnEnd; const char * szHostPtr; char szHostsList[0x100] = { 0 }; char szHostPath[0x80] = { 0 }; // Retrieve both strings if (Csv.GetString(szHostsList, sizeof(szHostsList) - 1, Indices[0]) != ERROR_SUCCESS) return NULL; if (Csv.GetString(szHostPath, sizeof(szHostPath), Indices[1]) != ERROR_SUCCESS) return NULL; // Did we retrieve something? if (szHostsList[0] != 0) { size_t nCombinedLength; size_t nHostsLength = strlen(szHostsList) + 1; size_t nPathLength = strlen(szHostPath); size_t cbToAllocate; size_t nHostsCount = 1; // Cut the hosts list to pieces for (size_t i = 0; i < nHostsLength; i++) { if (szHostsList[i] == ' ') { szHostsList[i] = 0; nHostsCount++; } } // Merge the hosts list cbToAllocate = nHostsLength + (nPathLength + 1) * nHostsCount + 1; szCdnList = szCdnPtr = CASC_ALLOC(TCHAR, cbToAllocate); szCdnEnd = szCdnList + cbToAllocate - 1; if (szCdnList != NULL) { for (szHostPtr = szHostsList; szHostPtr[0] != 0; szHostPtr += strlen(szHostPtr) + 1) { nCombinedLength = CombineUrlPath(szCdnPtr, (szCdnEnd - szCdnPtr), szHostPtr, szHostPath); if (nCombinedLength == 0) break; szCdnPtr += nCombinedLength + 1; szCdnPtr[0] = 0; } } } return szCdnList; }
int RootHandler_CreateWoW6(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile, DWORD dwLocaleMask) { TRootHandler_WoW6 * pRootHandler; LPBYTE pbRootFileEnd = pbRootFile + cbRootFile; int nError; // Verify the size if(pbRootFile == NULL || cbRootFile <= sizeof(PFILE_LOCALE_BLOCK)) nError = ERROR_FILE_CORRUPT; // Allocate the root handler object hs->pRootHandler = pRootHandler = CASC_ALLOC(TRootHandler_WoW6, 1); if(pRootHandler == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Fill-in the handler functions memset(pRootHandler, 0, sizeof(TRootHandler_WoW6)); pRootHandler->Insert = (ROOT_INSERT)WowHandler_Insert; pRootHandler->Search = (ROOT_SEARCH)WowHandler_Search; pRootHandler->EndSearch = (ROOT_ENDSEARCH)WowHandler_EndSearch; pRootHandler->GetKey = (ROOT_GETKEY)WowHandler_GetKey; pRootHandler->Close = (ROOT_CLOSE)WowHandler_Close; #ifdef _DEBUG pRootHandler->Dump = TRootHandlerWoW6_Dump; // Support for ROOT file dump #endif // _DEBUG // Count the files that are going to be loaded ParseWowRootFile(pRootHandler, ParseRoot_CountFiles, pbRootFile, pbRootFileEnd, dwLocaleMask); pRootHandler->dwTotalFileCount += CASC_EXTRA_FILES; // Create linear table that will contain all root items nError = Array_Create(&pRootHandler->FileTable, CASC_FILE_ENTRY, pRootHandler->dwTotalFileCount); if(nError != ERROR_SUCCESS) return nError; // Create sorted table that will contain all root items to lookup by FileDataId nError = Array_Create(&pRootHandler->FileDataIdLookupTable, PCASC_FILE_ENTRY, pRootHandler->dwTotalFileCount); if (nError != ERROR_SUCCESS) return nError; // Create the map of FileHash ->FileEntry pRootHandler->pRootMap = Map_Create(pRootHandler->dwTotalFileCount, sizeof(ULONGLONG), FIELD_OFFSET(CASC_FILE_ENTRY, FileNameHash)); if(pRootHandler->pRootMap == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Parse the root file again and insert all files to the map ParseWowRootFile(pRootHandler, ParseRoot_AddRootEntries, pbRootFile, pbRootFileEnd, dwLocaleMask); // Sort entries by FileDataId for searches qsort_pointer_array((void**)pRootHandler->FileDataIdLookupTable.ItemArray, pRootHandler->FileDataIdLookupTable.ItemCount, &FileDataIdCompare, NULL); return ERROR_SUCCESS; }
static int LoadSingleBlob(PQUERY_KEY pBlob, LPBYTE pbBlobBegin, LPBYTE pbBlobEnd) { LPBYTE pbBuffer; size_t nLength = (pbBlobEnd - pbBlobBegin) / 2; // Check maximum size if(nLength > MAX_CASC_KEY_LENGTH) return ERROR_INVALID_PARAMETER; // Allocate the blob buffer pbBuffer = CASC_ALLOC(BYTE, MAX_CASC_KEY_LENGTH); if(pbBuffer == NULL) return ERROR_NOT_ENOUGH_MEMORY; return LoadBlobArray(pBlob, 1, pbBlobBegin, pbBlobEnd, pbBuffer, MAX_CASC_KEY_LENGTH); }
static int LoadMultipleBlobs(PQUERY_KEY pBlob, const char * szLineBegin, const char * szLineEnd, DWORD dwBlobCount) { size_t nLength = (szLineEnd - szLineBegin); // We expect each blob to have length of the encoding key and one space between if(nLength > (dwBlobCount * MD5_STRING_SIZE) + ((dwBlobCount - 1) * sizeof(char))) return ERROR_INVALID_PARAMETER; // Allocate the blob buffer pBlob->pbData = CASC_ALLOC(BYTE, dwBlobCount * MD5_HASH_SIZE); if(pBlob->pbData == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Set the buffer size and load the blob array pBlob->cbData = dwBlobCount * MD5_HASH_SIZE; return LoadBlobArray(pBlob, szLineBegin, szLineEnd, dwBlobCount); }
static PLISTFILE_CACHE CreateListFileCache(DWORD dwFileSize) { PLISTFILE_CACHE pCache; // Allocate cache for one file block pCache = (PLISTFILE_CACHE)CASC_ALLOC(BYTE, sizeof(LISTFILE_CACHE) + dwFileSize); if(pCache != NULL) { // Set the initial pointers pCache->pBegin = pCache->pPos = (char *)(pCache + 1); pCache->pEnd = pCache->pBegin + dwFileSize; } // Return the cache return pCache; }
static TCascFile * CreateFileHandle(TCascStorage * hs, PQUERY_KEY pCKey, PQUERY_KEY pEKey, PCASC_EKEY_ENTRY pEKeyEntry, DWORD dwOpenFlags, DWORD dwContentSize) { ULONGLONG StorageOffset = ConvertBytesToInteger_5(pEKeyEntry->StorageOffset); ULONGLONG FileOffsMask = ((ULONGLONG)1 << hs->IndexFile[0].FileOffsetBits) - 1; TCascFile * hf; // Allocate the CASC file structure hf = (TCascFile *)CASC_ALLOC(TCascFile, 1); if(hf != NULL) { // Initialize the structure memset(hf, 0, sizeof(TCascFile)); hf->ArchiveIndex = (DWORD)(StorageOffset >> hs->IndexFile[0].FileOffsetBits); hf->ArchiveOffset = (DWORD)(StorageOffset & FileOffsMask); hf->szClassName = "TCascFile"; hf->OpenFlags = dwOpenFlags; // Supply the content key, if available if(pCKey != NULL) { assert(pCKey->pbData != NULL && pCKey->cbData == MD5_HASH_SIZE); memcpy(hf->CKey.Value, pCKey->pbData, pCKey->cbData); } // Supply the encoded key, if available if(pEKey != NULL) { assert(pEKey->pbData != NULL && CASC_EKEY_SIZE <= pEKey->cbData && pEKey->cbData <= MD5_HASH_SIZE); memcpy(hf->EKey.Value, pEKey->pbData, pEKey->cbData); } // Copy the encoded file size if(pEKeyEntry != NULL) { hf->EncodedSize = ConvertBytesToInteger_4_LE(pEKeyEntry->EncodedSize); } // Set the content size hf->ContentSize = dwContentSize; // Increment the number of references to the archive hs->dwRefCount++; hf->hs = hs; }
static PLISTFILE_MAP ListMap_Create() { PLISTFILE_MAP pListMap; size_t cbToAllocate; // Create buffer for the listfile // Note that because the listfile is quite big and CASC_REALLOC // is a costly operation, we want to have as few reallocs as possible. cbToAllocate = sizeof(LISTFILE_MAP) + LISTMAP_INITIAL; pListMap = (PLISTFILE_MAP)CASC_ALLOC(BYTE, cbToAllocate); if(pListMap != NULL) { // Fill the listfile buffer memset(pListMap, 0, sizeof(LISTFILE_MAP)); pListMap->cbBufferMax = LISTMAP_INITIAL; } return pListMap; }
static int EnsureFrameHeadersLoaded(TCascFile * hf) { int nError; // Make sure we have header area loaded nError = EnsureHeaderAreaIsLoaded(hf); if(nError != ERROR_SUCCESS) return nError; // If the frame headers are not loaded yet, do it if(hf->pFrames == NULL) { // Allocate the frame array hf->pFrames = CASC_ALLOC(CASC_FILE_FRAME, hf->FrameCount); if(hf->pFrames != NULL) { // Either load the frames from the file or supply them on our own if(hf->HeaderSize != 0) { hf->FramesOffset = hf->HeaderOffset + sizeof(DWORD) + sizeof(DWORD) + sizeof(DWORD); nError = LoadFileFrames(hf); } else { // Offset of the first frame is right after the file frames hf->FramesOffset = hf->HeaderOffset + sizeof(DWORD) + sizeof(DWORD); hf->pFrames[0].FrameArchiveOffset = hf->FramesOffset; hf->pFrames[0].FrameFileOffset = 0; hf->pFrames[0].CompressedSize = hf->CompressedSize; hf->pFrames[0].FrameSize = hf->FileSize; memset(hf->pFrames[0].md5, 0, MD5_HASH_SIZE); } } // Return result return (hf->pFrames != NULL) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT; } return ERROR_SUCCESS; }
static int LoadTextFile(const TCHAR * szFileName, PQUERY_KEY pFileBlob) { TFileStream * pStream; ULONGLONG FileSize = 0; int nError = ERROR_SUCCESS; // Open the agent file pStream = FileStream_OpenFile(szFileName, STREAM_FLAG_READ_ONLY | STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE); if(pStream != NULL) { // Retrieve its size FileStream_GetSize(pStream, &FileSize); // Load the file to memory if(0 < FileSize && FileSize < 0x100000) { // Initialize the blob pFileBlob->cbData = (DWORD)FileSize; pFileBlob->pbData = CASC_ALLOC(BYTE, pFileBlob->cbData + 1); // Load the file data into the blob if(pFileBlob->pbData != NULL) { FileStream_Read(pStream, NULL, pFileBlob->pbData, (DWORD)FileSize); pFileBlob->pbData[pFileBlob->cbData] = 0; } else nError = ERROR_NOT_ENOUGH_MEMORY; } else nError = ERROR_INVALID_PARAMETER; FileStream_Close(pStream); } else nError = GetLastError(); return nError; }
static int LoadMultipleHashes(PQUERY_KEY pBlob, const char * szLineBegin, const char * szLineEnd) { size_t HashCount = 0; int nError = ERROR_SUCCESS; // Retrieve the hash count if (CaptureHashCount(szLineBegin, szLineEnd, &HashCount) == NULL) return ERROR_BAD_FORMAT; // Do nothing if there is no data if(HashCount != 0) { // Allocate the blob buffer pBlob->pbData = CASC_ALLOC(BYTE, HashCount * MD5_HASH_SIZE); if(pBlob->pbData == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Set the buffer size and load the blob array pBlob->cbData = (DWORD)(HashCount * MD5_HASH_SIZE); nError = LoadHashArray(pBlob, szLineBegin, szLineEnd, HashCount); } return nError; }
static int ParseCoreTOC( TRootHandler_Diablo3 * pRootHandler, PCASC_MAP pPackageMap, LPBYTE pbCoreTocFile, LPBYTE pbCoreTocEnd) { PDIABLO3_CORE_TOC_HEADER pTocHeader; PDIABLO3_CORE_TOC_ENTRY pSortedEntries; PDIABLO3_CORE_TOC_ENTRY pTocEntry; LPBYTE pbCoreTocNames; DWORD dwFileIndexes = 0; DWORD i; // Check the space for header if((pbCoreTocFile + sizeof(DIABLO3_CORE_TOC_HEADER)) > pbCoreTocEnd) return ERROR_FILE_CORRUPT; pTocHeader = (PDIABLO3_CORE_TOC_HEADER)pbCoreTocFile; pbCoreTocFile += sizeof(DIABLO3_CORE_TOC_HEADER); // Calculate space needed for allocation for(i = 0; i < DIABLO3_MAX_ASSETS; i++) { // Get the first entry pTocEntry = (PDIABLO3_CORE_TOC_ENTRY)(pbCoreTocFile + pTocHeader->EntryOffsets[i]); // Find out the entry with the maximum index for(DWORD n = 0; n < pTocHeader->EntryCounts[i]; n++) { if(pTocEntry->FileIndex >= dwFileIndexes) dwFileIndexes = pTocEntry->FileIndex + 1; pTocEntry++; } } // Allocate and populate the array of DIABLO3_CORE_TOC_ENTRYs pSortedEntries = CASC_ALLOC(DIABLO3_CORE_TOC_ENTRY, dwFileIndexes); if(pSortedEntries != NULL) { // Initialize all entries to invalid memset(pSortedEntries, 0xFF, dwFileIndexes * sizeof(DIABLO3_CORE_TOC_ENTRY)); // Populate the linear array with the entries for(i = 0; i < DIABLO3_MAX_ASSETS; i++) { // Set the pointers pTocEntry = (PDIABLO3_CORE_TOC_ENTRY)(pbCoreTocFile + pTocHeader->EntryOffsets[i]); pbCoreTocNames = (LPBYTE)(pTocEntry + pTocHeader->EntryCounts[i]); // Setup the entries for(DWORD n = 0; n < pTocHeader->EntryCounts[i]; n++) { pSortedEntries[pTocEntry->FileIndex].AssetIndex = pTocEntry->AssetIndex; pSortedEntries[pTocEntry->FileIndex].FileIndex = pTocEntry->FileIndex; pSortedEntries[pTocEntry->FileIndex].NameOffset = (DWORD)(pbCoreTocNames - pbCoreTocFile) + pTocEntry->NameOffset; pTocEntry++; } } // Now use the linear array to resolve the asset indexes and plain names ResolveFullFileNames(pRootHandler, pSortedEntries, pPackageMap, pbCoreTocFile, dwFileIndexes); CASC_FREE(pSortedEntries); } return ERROR_SUCCESS; }
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; }
bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDWORD pdwBytesRead) { PCASC_FILE_FRAME pFrame = NULL; ULONGLONG FileOffset; TCascFile * hf; LPBYTE pbBuffer = (LPBYTE)pvBuffer; DWORD dwStartPointer = 0; DWORD dwFilePointer = 0; DWORD dwEndPointer = 0; DWORD cbOutBuffer; int nError = ERROR_SUCCESS; // The buffer must be valid if(pvBuffer == NULL) { SetLastError(ERROR_INVALID_PARAMETER); return false; } // Validate the file handle if((hf = IsValidFileHandle(hFile)) == NULL) { SetLastError(ERROR_INVALID_HANDLE); return false; } // If the file frames are not loaded yet, do it now if(nError == ERROR_SUCCESS) { nError = EnsureFrameHeadersLoaded(hf); } // If the file position is at or beyond end of file, do nothing if(nError == ERROR_SUCCESS && hf->FilePointer >= hf->FileSize) { *pdwBytesRead = 0; return ERROR_SUCCESS; } // Find the file frame where to read from if(nError == ERROR_SUCCESS) { // Get the frame pFrame = FindFileFrame(hf, hf->FilePointer); if(pFrame == NULL) nError = ERROR_FILE_CORRUPT; } // Perform the read if(nError == ERROR_SUCCESS) { // If not enough bytes in the file remaining, cut them dwStartPointer = dwFilePointer = hf->FilePointer; dwEndPointer = dwStartPointer + dwBytesToRead; if(dwEndPointer > hf->FileSize) dwEndPointer = hf->FileSize; // Perform block read from each file frame while(dwFilePointer < dwEndPointer) { LPBYTE pbRawData = NULL; DWORD dwFrameStart = pFrame->FrameFileOffset; DWORD dwFrameEnd = pFrame->FrameFileOffset + pFrame->FrameSize; // Shall we populate the cache with a new data? if(dwFrameStart != hf->CacheStart || hf->CacheEnd != dwFrameEnd) { // Shall we reallocate the cache buffer? if(pFrame->FrameSize > hf->cbFileCache) { if(hf->pbFileCache != NULL) CASC_FREE(hf->pbFileCache); hf->pbFileCache = CASC_ALLOC(BYTE, pFrame->FrameSize); hf->cbFileCache = pFrame->FrameSize; } // We also need to allocate buffer for the raw data pbRawData = CASC_ALLOC(BYTE, pFrame->CompressedSize); if(pbRawData == NULL) { nError = ERROR_NOT_ENOUGH_MEMORY; break; } // Load the raw file data to memory FileOffset = pFrame->FrameArchiveOffset; if(!FileStream_Read(hf->pStream, &FileOffset, pbRawData, pFrame->CompressedSize)) { CASC_FREE(pbRawData); nError = GetLastError(); break; } // Verify the block MD5 if(!VerifyDataBlockHash(pbRawData, pFrame->CompressedSize, pFrame->md5)) { CASC_FREE(pbRawData); nError = ERROR_FILE_CORRUPT; break; } // Decompress the file frame cbOutBuffer = pFrame->FrameSize; nError = CascDecompress(hf->pbFileCache, &cbOutBuffer, pbRawData, pFrame->CompressedSize); if(nError != ERROR_SUCCESS || cbOutBuffer != pFrame->FrameSize) { CASC_FREE(pbRawData); nError = ERROR_FILE_CORRUPT; break; } // Set the start and end of the cache hf->CacheStart = dwFrameStart; hf->CacheEnd = dwFrameEnd; // Free the decompress buffer, if needed CASC_FREE(pbRawData); } // Copy the decompressed data if(dwFrameEnd > dwEndPointer) dwFrameEnd = dwEndPointer; memcpy(pbBuffer, hf->pbFileCache + (dwFilePointer - dwFrameStart), (dwFrameEnd - dwFilePointer)); pbBuffer += (dwFrameEnd - dwFilePointer); // Move pointers dwFilePointer = dwFrameEnd; pFrame++; } } // Update the file position if(nError == ERROR_SUCCESS) { if(pdwBytesRead != NULL) *pdwBytesRead = (dwFilePointer - dwStartPointer); hf->FilePointer = dwFilePointer; } if(nError != ERROR_SUCCESS) SetLastError(nError); return (nError == ERROR_SUCCESS); }
static int ProcessFileFrame( LPBYTE pbOutBuffer, DWORD cbOutBuffer, LPBYTE pbInBuffer, DWORD cbInBuffer, DWORD dwFrameIndex) { LPBYTE pbTempBuffer; LPBYTE pbWorkBuffer; DWORD cbTempBuffer = CASCLIB_MAX(cbInBuffer, cbOutBuffer); DWORD cbWorkBuffer = cbOutBuffer + 1; DWORD dwStepCount = 0; bool bWorkComplete = false; int nError = ERROR_SUCCESS; // Allocate the temporary buffer that will serve as output pbWorkBuffer = pbTempBuffer = CASC_ALLOC(BYTE, cbTempBuffer); if(pbWorkBuffer == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Perform the loop for(;;) { // Set the output buffer. // Even operations: extract to temporary buffer // Odd operations: extract to output buffer pbWorkBuffer = (dwStepCount & 0x01) ? pbOutBuffer : pbTempBuffer; cbWorkBuffer = (dwStepCount & 0x01) ? cbOutBuffer : cbTempBuffer; // Perform the operation specific to the operation ID switch(pbInBuffer[0]) { case 'E': // Encrypted files nError = CascDecrypt(pbWorkBuffer, &cbWorkBuffer, pbInBuffer + 1, cbInBuffer - 1, dwFrameIndex); bWorkComplete = (nError != ERROR_SUCCESS); break; case 'Z': // ZLIB compressed files nError = CascDecompress(pbWorkBuffer, &cbWorkBuffer, pbInBuffer + 1, cbInBuffer - 1); bWorkComplete = true; break; case 'N': // Normal stored files nError = CascDirectCopy(pbWorkBuffer, &cbWorkBuffer, pbInBuffer + 1, cbInBuffer - 1); bWorkComplete = true; break; case 'F': // Recursive frames - not supported default: // Unrecognized - if we unpacked something, we consider it done nError = ERROR_NOT_SUPPORTED; bWorkComplete = true; assert(false); break; } // Are we done? if(bWorkComplete) break; // Set the input buffer to the work buffer pbInBuffer = pbWorkBuffer; cbInBuffer = cbWorkBuffer; dwStepCount++; } // If the data are currently in the temporary buffer, // we need to copy them to output buffer if(nError == ERROR_SUCCESS && pbWorkBuffer != pbOutBuffer) { if(cbWorkBuffer != cbOutBuffer) nError = ERROR_INSUFFICIENT_BUFFER; memcpy(pbOutBuffer, pbWorkBuffer, cbOutBuffer); } // Free the temporary buffer CASC_FREE(pbTempBuffer); return nError; }
bool WINAPI CascReadFile(HANDLE hFile, void * pvBuffer, DWORD dwBytesToRead, PDWORD pdwBytesRead) { PCASC_FILE_FRAME pFrame = NULL; ULONGLONG StreamSize; ULONGLONG FileOffset; TCascFile * hf; LPBYTE pbBuffer = (LPBYTE)pvBuffer; DWORD dwStartPointer = 0; DWORD dwFilePointer = 0; DWORD dwEndPointer = 0; DWORD dwFrameSize; bool bReadResult; int nError = ERROR_SUCCESS; // The buffer must be valid if(pvBuffer == NULL) { SetLastError(ERROR_INVALID_PARAMETER); return false; } // Validate the file handle if((hf = IsValidFileHandle(hFile)) == NULL) { SetLastError(ERROR_INVALID_HANDLE); return false; } // If the file frames are not loaded yet, do it now if(nError == ERROR_SUCCESS) { nError = EnsureFrameHeadersLoaded(hf); } // If the file position is at or beyond end of file, do nothing if(nError == ERROR_SUCCESS && hf->FilePointer >= hf->FileSize) { *pdwBytesRead = 0; return ERROR_SUCCESS; } // Find the file frame where to read from if(nError == ERROR_SUCCESS) { // Get the frame pFrame = FindFileFrame(hf, hf->FilePointer); if(pFrame == NULL || pFrame->CompressedSize < 1) nError = ERROR_FILE_CORRUPT; } // Perform the read if(nError == ERROR_SUCCESS) { // If not enough bytes in the file remaining, cut them dwStartPointer = dwFilePointer = hf->FilePointer; dwEndPointer = dwStartPointer + dwBytesToRead; if(dwEndPointer > hf->FileSize) dwEndPointer = hf->FileSize; // Perform block read from each file frame while(dwFilePointer < dwEndPointer) { LPBYTE pbFrameData = NULL; DWORD dwFrameStart = pFrame->FrameFileOffset; DWORD dwFrameEnd = pFrame->FrameFileOffset + pFrame->FrameSize; // Shall we populate the cache with a new data? if(dwFrameStart != hf->CacheStart || hf->CacheEnd != dwFrameEnd) { // Shall we reallocate the cache buffer? if(pFrame->FrameSize > hf->cbFileCache) { if(hf->pbFileCache != NULL) CASC_FREE(hf->pbFileCache); hf->pbFileCache = CASC_ALLOC(BYTE, pFrame->FrameSize); hf->cbFileCache = pFrame->FrameSize; } // We also need to allocate buffer for the raw data pbFrameData = CASC_ALLOC(BYTE, pFrame->CompressedSize); if(pbFrameData == NULL) { nError = ERROR_NOT_ENOUGH_MEMORY; break; } // Load the raw file data to memory FileOffset = pFrame->FrameArchiveOffset; bReadResult = FileStream_Read(hf->pStream, &FileOffset, pbFrameData, pFrame->CompressedSize); // Note: The raw file data size could be less than expected // Happened in WoW build 19342 with the ROOT file. MD5 in the frame header // is zeroed, which means it should not be checked // Frame File: data.029 // Frame Offs: 0x013ED9F0 size 0x01325B32 // Frame End: 0x02713522 // File Size: 0x027134FC if(bReadResult == false && GetLastError() == ERROR_HANDLE_EOF && !IsValidMD5(pFrame->md5)) { // Get the size of the remaining file FileStream_GetSize(hf->pStream, &StreamSize); dwFrameSize = (DWORD)(StreamSize - FileOffset); // If the frame offset is before EOF and frame end is beyond EOF, correct it if(FileOffset < StreamSize && dwFrameSize < pFrame->CompressedSize) { memset(pbFrameData + dwFrameSize, 0, (pFrame->CompressedSize - dwFrameSize)); bReadResult = true; } } // If the read result failed, we cannot finish reading it if(bReadResult && VerifyDataBlockHash(pbFrameData, pFrame->CompressedSize, pFrame->md5)) { // Convert the source frame to the file cache nError = ProcessFileFrame(hf->pbFileCache, pFrame->FrameSize, pbFrameData, pFrame->CompressedSize, (DWORD)(pFrame - hf->pFrames)); if(nError == ERROR_SUCCESS) { // Set the start and end of the cache hf->CacheStart = dwFrameStart; hf->CacheEnd = dwFrameEnd; } } else { nError = ERROR_FILE_CORRUPT; } // Free the raw frame data CASC_FREE(pbFrameData); } // Copy the decompressed data if(dwFrameEnd > dwEndPointer) dwFrameEnd = dwEndPointer; memcpy(pbBuffer, hf->pbFileCache + (dwFilePointer - dwFrameStart), (dwFrameEnd - dwFilePointer)); pbBuffer += (dwFrameEnd - dwFilePointer); // Move pointers dwFilePointer = dwFrameEnd; pFrame++; } } // Update the file position if(nError == ERROR_SUCCESS) { if(pdwBytesRead != NULL) *pdwBytesRead = (dwFilePointer - dwStartPointer); hf->FilePointer = dwFilePointer; } if(nError != ERROR_SUCCESS) SetLastError(nError); return (nError == ERROR_SUCCESS); }
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; }
int RootHandler_CreateDiablo3(TCascStorage * hs, LPBYTE pbRootFile, DWORD cbRootFile) { TRootHandler_Diablo3 * pRootHandler; PCASC_MAP pPackageMap = NULL; LPBYTE pbRootFileEnd = pbRootFile + cbRootFile; LPBYTE pbPackagesDat = NULL; DWORD dwTotalFileCount; DWORD cbPackagesDat = 0; int nError; // Allocate the root handler object hs->pRootHandler = pRootHandler = CASC_ALLOC(TRootHandler_Diablo3, 1); if(pRootHandler == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Fill-in the handler functions memset(pRootHandler, 0, sizeof(TRootHandler_Diablo3)); pRootHandler->Insert = (ROOT_INSERT)D3Handler_Insert; pRootHandler->Search = (ROOT_SEARCH)D3Handler_Search; pRootHandler->EndSearch = (ROOT_ENDSEARCH)D3Handler_EndSearch; pRootHandler->GetKey = (ROOT_GETKEY)D3Handler_GetKey; pRootHandler->Close = (ROOT_CLOSE)D3Handler_Close; pRootHandler->GetFileId = (ROOT_GETFILEID)D3Handler_GetFileId; // Fill-in the flags pRootHandler->dwRootFlags |= ROOT_FLAG_HAS_NAMES; // Scan the total number of files in the root directories // Reserve space for extra files dwTotalFileCount = ScanDirectoryFile(hs, pbRootFile, pbRootFileEnd); if(dwTotalFileCount == 0) return ERROR_FILE_CORRUPT; dwTotalFileCount += CASC_EXTRA_FILES; // Allocate the global linear file table // Note: This is about 18 MB of memory for Diablo III PTR build 30013 nError = Array_Create(&pRootHandler->FileTable, CASC_FILE_ENTRY, dwTotalFileCount); if(nError != ERROR_SUCCESS) return nError; // Allocate global buffer for file names. // The size of the buffer was taken from Diablo III build 30013 nError = Array_Create(&pRootHandler->FileNames, char, 0x01000000); if(nError != ERROR_SUCCESS) return nError; // Create map of ROOT_ENTRY -> FileEntry pRootHandler->pRootMap = Map_Create(dwTotalFileCount, sizeof(ULONGLONG), FIELD_OFFSET(CASC_FILE_ENTRY, FileNameHash)); if(pRootHandler->pRootMap == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Parse the ROOT file and insert all entries in the file table nError = ParseDirectoryFile(pRootHandler, pbRootFile, pbRootFileEnd, DIABLO3_INVALID_INDEX); if(nError == ERROR_SUCCESS) { size_t dwRootEntries = pRootHandler->FileTable.ItemCount; // We expect the number of level-0 to be less than maximum assert(dwRootEntries < DIABLO3_MAX_SUBDIRS); // Now parse the all root items and load them for(DWORD i = 0; i < dwRootEntries; i++) { PCASC_FILE_ENTRY pRootEntry = (PCASC_FILE_ENTRY)Array_ItemAt(&pRootHandler->FileTable, i); // Load the entire file to memory pbRootFile = LoadFileToMemory(hs, pRootEntry->EncodingKey.Value, &cbRootFile); if(pbRootFile != NULL) { nError = ParseDirectoryFile(pRootHandler, pbRootFile, pbRootFile + cbRootFile, i); CASC_FREE(pbRootFile); } } } // Note: The file "Base\Data_D3\PC\Misc\Packages.dat" contains the names // of the files (without level-0 and level-1 directory). We can use these // names for supplying the missing extensions if(nError == ERROR_SUCCESS) { // Load the entire file to memory pbPackagesDat = LoadFileToMemory(hs, "Base\\Data_D3\\PC\\Misc\\Packages.dat", &cbPackagesDat); if(pbPackagesDat != NULL) { pPackageMap = CreatePackageMap(pbPackagesDat, pbPackagesDat + cbPackagesDat); } } // Vast majorify of files at this moment don't have names. // We can load the Base\CoreTOC.dat file in order // to get directory asset indexes, file names and extensions if(nError == ERROR_SUCCESS) { LPBYTE pbCoreTOC; DWORD cbCoreTOC = 0; // Load the entire file to memory pbCoreTOC = LoadFileToMemory(hs, "Base\\CoreTOC.dat", &cbCoreTOC); if(pbCoreTOC != NULL) { ParseCoreTOC(pRootHandler, pPackageMap, pbCoreTOC, pbCoreTOC + cbCoreTOC); CASC_FREE(pbCoreTOC); } } // Free the packages map if(pPackageMap != NULL) Map_Free(pPackageMap); if(pbPackagesDat != NULL) CASC_FREE(pbPackagesDat); return nError; }