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; }
static int ApplyFilePatch( TMPQFile * hfBase, // The file in the base MPQ TMPQFile * hfPrev, // The file in the previous MPQ TMPQFile * hf) { TPatchHeader * pPatchHeader = hf->pPatchHeader; TMPQFile * hfFrom = NULL; int nError = ERROR_SUCCESS; // Sanity checks assert(hf->pbFileData == NULL); // Either take the base version or the previous version if(!memcmp(hfBase->FileDataMD5, pPatchHeader->md5_before_patch, MD5_DIGEST_SIZE)) hfFrom = hfBase; if(!memcmp(hfPrev->FileDataMD5, pPatchHeader->md5_before_patch, MD5_DIGEST_SIZE)) hfFrom = hfPrev; if(hfFrom == NULL) return ERROR_FILE_CORRUPT; // Allocate the buffer for patched file content hf->pbFileData = STORM_ALLOC(BYTE, pPatchHeader->dwSizeAfterPatch); hf->cbFileData = pPatchHeader->dwSizeAfterPatch; if(hf->pbFileData == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Apply the patch if(nError == ERROR_SUCCESS) { switch(pPatchHeader->dwPatchType) { case 0x59504f43: // 'COPY' nError = ApplyFilePatch_COPY(hfFrom, hf, pPatchHeader); break; case 0x30445342: // 'BSD0' nError = ApplyFilePatch_BSD0(hfFrom, hf, pPatchHeader); break; default: nError = ERROR_FILE_CORRUPT; break; } } // Verify MD5 after patch if(nError == ERROR_SUCCESS && pPatchHeader->dwSizeAfterPatch != 0) { // Verify the patched file if(!VerifyDataBlockHash(hf->pbFileData, hf->cbFileData, pPatchHeader->md5_after_patch)) nError = ERROR_FILE_CORRUPT; // Copy the MD5 of the new block memcpy(hf->FileDataMD5, pPatchHeader->md5_after_patch, MD5_DIGEST_SIZE); } return nError; }
// Performs the MD5-based check on the listfile bool ListFile_VerifyMD5(void * pvListFile, LPBYTE pbHashMD5) { PLISTFILE_CACHE pCache = (PLISTFILE_CACHE)pvListFile; // Must be at the beginning assert(pCache->pPos == pCache->pBegin); // Verify the MD5 hash for the entire block return VerifyDataBlockHash(pCache->pBegin, (DWORD)(pCache->pEnd - pCache->pBegin), pbHashMD5); }
static int ApplyMpqPatch( TMPQFile * hf, TPatchHeader * pPatchHeader) { int nError = ERROR_SUCCESS; // Verify the original file before patching if(pPatchHeader->dwSizeBeforePatch != 0) { if(!VerifyDataBlockHash(hf->pbFileData, hf->cbFileData, pPatchHeader->md5_before_patch)) nError = ERROR_FILE_CORRUPT; } // Apply the patch if(nError == ERROR_SUCCESS) { switch(pPatchHeader->dwPatchType) { case 0x59504f43: // 'COPY' nError = ApplyMpqPatch_COPY(hf, pPatchHeader); break; case 0x30445342: // 'BSD0' nError = ApplyMpqPatch_BSD0(hf, pPatchHeader); break; default: nError = ERROR_FILE_CORRUPT; break; } } // Verify MD5 after patch if(nError == ERROR_SUCCESS && pPatchHeader->dwSizeAfterPatch != 0) { // Verify the patched file if(!VerifyDataBlockHash(hf->pbFileData, hf->cbFileData, pPatchHeader->md5_after_patch)) nError = ERROR_FILE_CORRUPT; } return nError; }
static int ApplyFilePatch( TMPQPatcher * pPatcher, PMPQ_PATCH_HEADER pFullPatch) { LPBYTE pbSource = (pPatcher->nCounter & 0x1) ? pPatcher->pbFileData2 : pPatcher->pbFileData1; LPBYTE pbTarget = (pPatcher->nCounter & 0x1) ? pPatcher->pbFileData1 : pPatcher->pbFileData2; int nError; // Sanity checks assert(pFullPatch->dwSizeAfterPatch <= pPatcher->cbMaxFileData); // Apply the patch according to the type switch(pFullPatch->dwPatchType) { case 0x59504f43: // 'COPY' nError = ApplyFilePatch_COPY(pPatcher, pFullPatch, pbTarget, pbSource); break; case 0x30445342: // 'BSD0' nError = ApplyFilePatch_BSD0(pPatcher, pFullPatch, pbTarget, pbSource); break; default: nError = ERROR_FILE_CORRUPT; break; } // Verify MD5 after patch if(nError == ERROR_SUCCESS && pFullPatch->dwSizeAfterPatch != 0) { // Verify the patched file if(!VerifyDataBlockHash(pbTarget, pFullPatch->dwSizeAfterPatch, pFullPatch->md5_after_patch)) nError = ERROR_FILE_CORRUPT; // Copy the MD5 of the new block memcpy(pPatcher->this_md5, pFullPatch->md5_after_patch, MD5_DIGEST_SIZE); } 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); }
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); }