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 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; DWORD cbOutBuffer; 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) 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; bReadResult = FileStream_Read(hf->pStream, &FileOffset, pbRawData, 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(pbRawData + dwFrameSize, 0, (pFrame->CompressedSize - dwFrameSize)); bReadResult = true; } } // If the read result failed, we cannot finish reading it if(bReadResult == false) { 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); }