/*****************************************************************************/ /* SFileReadFile.cpp Copyright (c) Ladislav Zezula 2003 */ /*---------------------------------------------------------------------------*/ /* Description : */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* xx.xx.99 1.00 Lad The first version of SFileReadFile.cpp */ /* 24.03.99 1.00 Lad Added the SFileGetFileInfo function */ /*****************************************************************************/ #define __STORMLIB_SELF__ #include "StormLib.h" #include "StormCommon.h" //----------------------------------------------------------------------------- // Local functions // hf - MPQ File handle. // pbBuffer - Pointer to target buffer to store sectors. // dwByteOffset - Position of sector in the file (relative to file begin) // dwBytesToRead - Number of bytes to read. Must be multiplier of sector size. // pdwBytesRead - Stored number of bytes loaded static DWORD ReadMpqSectors(TMPQFile * hf, LPBYTE pbBuffer, DWORD dwByteOffset, DWORD dwBytesToRead, LPDWORD pdwBytesRead) { ULONGLONG RawFilePos; TMPQArchive * ha = hf->ha; TFileEntry * pFileEntry = hf->pFileEntry; LPBYTE pbRawSector = NULL; LPBYTE pbOutSector = pbBuffer; LPBYTE pbInSector = pbBuffer; DWORD dwRawBytesToRead; DWORD dwRawSectorOffset = dwByteOffset; DWORD dwSectorsToRead = dwBytesToRead / ha->dwSectorSize; DWORD dwSectorIndex = dwByteOffset / ha->dwSectorSize; DWORD dwSectorsDone = 0; DWORD dwBytesRead = 0; DWORD dwErrCode = ERROR_SUCCESS; // Note that dwByteOffset must be aligned to size of one sector // Note that dwBytesToRead must be a multiplier of one sector size // This is local function, so we won't check if that's true. // Note that files stored in single units are processed by a separate function // If there is not enough bytes remaining, cut dwBytesToRead if((dwByteOffset + dwBytesToRead) > hf->dwDataSize) dwBytesToRead = hf->dwDataSize - dwByteOffset; dwRawBytesToRead = dwBytesToRead; // Perform all necessary work to do with compressed files if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) { // If the sector positions are not loaded yet, do it if(hf->SectorOffsets == NULL) { dwErrCode = AllocateSectorOffsets(hf, true); if(dwErrCode != ERROR_SUCCESS || hf->SectorOffsets == NULL) return dwErrCode; } // If the sector checksums are not loaded yet, load them now. if(hf->SectorChksums == NULL && (pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC) && hf->bLoadedSectorCRCs == false) { // // Sector CRCs is plain crap feature. It is almost never present, // often it's empty, or the end offset of sector CRCs is zero. // We only try to load sector CRCs once, and regardless if it fails // or not, we won't try that again for the given file. // AllocateSectorChecksums(hf, true); hf->bLoadedSectorCRCs = true; } // TODO: If the raw data MD5s are not loaded yet, load them now // Only do it if the MPQ is of format 4.0 // if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_4 && ha->pHeader->dwRawChunkSize != 0) // { // dwErrCode = AllocateRawMD5s(hf, true); // if(dwErrCode != ERROR_SUCCESS) // return dwErrCode; // } // Assign the temporary buffer as target for read operation dwRawSectorOffset = hf->SectorOffsets[dwSectorIndex]; dwRawBytesToRead = hf->SectorOffsets[dwSectorIndex + dwSectorsToRead] - dwRawSectorOffset; // If the file is compressed, also allocate secondary buffer pbInSector = pbRawSector = STORM_ALLOC(BYTE, dwRawBytesToRead); if(pbRawSector == NULL) return ERROR_NOT_ENOUGH_MEMORY; } // Calculate raw file offset where the sector(s) are stored. RawFilePos = CalculateRawSectorOffset(hf, dwRawSectorOffset); // Set file pointer and read all required sectors if(FileStream_Read(ha->pStream, &RawFilePos, pbInSector, dwRawBytesToRead)) { // Now we have to decrypt and decompress all file sectors that have been loaded for(DWORD i = 0; i < dwSectorsToRead; i++) { DWORD dwRawBytesInThisSector = ha->dwSectorSize; DWORD dwBytesInThisSector = ha->dwSectorSize; DWORD dwIndex = dwSectorIndex + i; // If there is not enough bytes in the last sector, // cut the number of bytes in this sector if(dwRawBytesInThisSector > dwBytesToRead) dwRawBytesInThisSector = dwBytesToRead; if(dwBytesInThisSector > dwBytesToRead) dwBytesInThisSector = dwBytesToRead; // If the file is compressed, we have to adjust the raw sector size if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) dwRawBytesInThisSector = hf->SectorOffsets[dwIndex + 1] - hf->SectorOffsets[dwIndex]; // If the file is encrypted, we have to decrypt the sector if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) { BSWAP_ARRAY32_UNSIGNED(pbInSector, dwRawBytesInThisSector); // If we don't know the key, try to detect it by file content if(hf->dwFileKey == 0) { hf->dwFileKey = DetectFileKeyByContent(pbInSector, dwBytesInThisSector, hf->dwDataSize); if(hf->dwFileKey == 0) { dwErrCode = ERROR_UNKNOWN_FILE_KEY; break; } } DecryptMpqBlock(pbInSector, dwRawBytesInThisSector, hf->dwFileKey + dwIndex); BSWAP_ARRAY32_UNSIGNED(pbInSector, dwRawBytesInThisSector); } // If the file has sector CRC check turned on, perform it if(hf->bCheckSectorCRCs && hf->SectorChksums != NULL) { DWORD dwAdlerExpected = hf->SectorChksums[dwIndex]; DWORD dwAdlerValue = 0; // We can only check sector CRC when it's not zero // Neither can we check it if it's 0xFFFFFFFF. if(dwAdlerExpected != 0 && dwAdlerExpected != 0xFFFFFFFF) { dwAdlerValue = adler32(0, pbInSector, dwRawBytesInThisSector); if(dwAdlerValue != dwAdlerExpected) { dwErrCode = ERROR_CHECKSUM_ERROR; break; } } } // If the sector is really compressed, decompress it. // WARNING : Some sectors may not be compressed, it can be determined only // by comparing uncompressed and compressed size !!! if(dwRawBytesInThisSector < dwBytesInThisSector) { int cbOutSector = dwBytesInThisSector; int cbInSector = dwRawBytesInThisSector; int nResult = 0; // Is the file compressed by Blizzard's multiple compression ? if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS) { // Remember the last used compression hf->dwCompression0 = pbInSector[0]; // Decompress the data if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2) nResult = SCompDecompress2(pbOutSector, &cbOutSector, pbInSector, cbInSector); else nResult = SCompDecompress(pbOutSector, &cbOutSector, pbInSector, cbInSector); } // Is the file compressed by PKWARE Data Compression Library ? else if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE) { nResult = SCompExplode(pbOutSector, &cbOutSector, pbInSector, cbInSector); } // Did the decompression fail ? if(nResult == 0) { dwErrCode = ERROR_FILE_CORRUPT; break; } } else { if(pbOutSector != pbInSector) memcpy(pbOutSector, pbInSector, dwBytesInThisSector); } // Move pointers dwBytesToRead -= dwBytesInThisSector; dwByteOffset += dwBytesInThisSector; dwBytesRead += dwBytesInThisSector; pbOutSector += dwBytesInThisSector; pbInSector += dwRawBytesInThisSector; dwSectorsDone++; } } else { dwErrCode = GetLastError(); } // Free all used buffers if(pbRawSector != NULL) STORM_FREE(pbRawSector); // Give the caller thenumber of bytes read *pdwBytesRead = dwBytesRead; return dwErrCode; } static DWORD ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead) { ULONGLONG RawFilePos = hf->RawFilePos; TMPQArchive * ha = hf->ha; TFileEntry * pFileEntry = hf->pFileEntry; LPBYTE pbCompressed = NULL; LPBYTE pbRawData; DWORD dwErrCode = ERROR_SUCCESS; // If the file buffer is not allocated yet, do it. if(hf->pbFileSector == NULL) { dwErrCode = AllocateSectorBuffer(hf); if(dwErrCode != ERROR_SUCCESS || hf->pbFileSector == NULL) return dwErrCode; } // If the file is a patch file, adjust raw data offset if(hf->pPatchInfo != NULL) RawFilePos += hf->pPatchInfo->dwLength; pbRawData = hf->pbFileSector; // If the file sector is not loaded yet, do it if(hf->dwSectorOffs != 0) { // Is the file compressed? if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) { // Allocate space for compressed data pbCompressed = STORM_ALLOC(BYTE, pFileEntry->dwCmpSize); if(pbCompressed == NULL) return ERROR_NOT_ENOUGH_MEMORY; pbRawData = pbCompressed; } // Load the raw (compressed, encrypted) data if(!FileStream_Read(ha->pStream, &RawFilePos, pbRawData, pFileEntry->dwCmpSize)) { STORM_FREE(pbCompressed); return GetLastError(); } // If the file is encrypted, we have to decrypt the data first if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) { BSWAP_ARRAY32_UNSIGNED(pbRawData, pFileEntry->dwCmpSize); DecryptMpqBlock(pbRawData, pFileEntry->dwCmpSize, hf->dwFileKey); BSWAP_ARRAY32_UNSIGNED(pbRawData, pFileEntry->dwCmpSize); } // If the file is compressed, we have to decompress it now if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) { int cbOutBuffer = (int)hf->dwDataSize; int cbInBuffer = (int)pFileEntry->dwCmpSize; int nResult = 0; // // If the file is an incremental patch, the size of compressed data // is determined as pFileEntry->dwCmpSize - sizeof(TPatchInfo) // // In "wow-update-12694.MPQ" from Wow-Cataclysm BETA: // // File CmprSize DcmpSize DataSize Compressed? // -------------------------------------- ---------- -------- -------- --------------- // esES\DBFilesClient\LightSkyBox.dbc 0xBE->0xA2 0xBC 0xBC Yes // deDE\DBFilesClient\MountCapability.dbc 0x93->0x77 0x77 0x77 No // if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) cbInBuffer = cbInBuffer - sizeof(TPatchInfo); // Is the file compressed by Blizzard's multiple compression ? if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS) { // Remember the last used compression hf->dwCompression0 = pbRawData[0]; // Decompress the file if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2) nResult = SCompDecompress2(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer); else nResult = SCompDecompress(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer); } // Is the file compressed by PKWARE Data Compression Library ? // Note: Single unit files compressed with IMPLODE are not supported by Blizzard else if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE) nResult = SCompExplode(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer); dwErrCode = (nResult != 0) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT; } else { if(hf->pbFileSector != NULL && pbRawData != hf->pbFileSector) memcpy(hf->pbFileSector, pbRawData, hf->dwDataSize); } // Free the decompression buffer. if(pbCompressed != NULL) STORM_FREE(pbCompressed); // The file sector is now properly loaded hf->dwSectorOffs = 0; } // At this moment, we have the file loaded into the file buffer. // Copy as much as the caller wants if(dwErrCode == ERROR_SUCCESS && hf->dwSectorOffs == 0) { // File position is greater or equal to file size ? if(dwFilePos >= hf->dwDataSize) { *pdwBytesRead = 0; return ERROR_SUCCESS; } // If not enough bytes remaining in the file, cut them if((hf->dwDataSize - dwFilePos) < dwToRead) dwToRead = (hf->dwDataSize - dwFilePos); // Copy the bytes memcpy(pvBuffer, hf->pbFileSector + dwFilePos, dwToRead); // Give the number of bytes read *pdwBytesRead = dwToRead; } // An error, sorry return dwErrCode; } static DWORD ReadMpkFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead) { ULONGLONG RawFilePos = hf->RawFilePos + 0x0C; // For some reason, MPK files start at position (hf->RawFilePos + 0x0C) TMPQArchive * ha = hf->ha; TFileEntry * pFileEntry = hf->pFileEntry; LPBYTE pbCompressed = NULL; LPBYTE pbRawData = hf->pbFileSector; DWORD dwErrCode = ERROR_SUCCESS; // We do not support patch files in MPK archives assert(hf->pPatchInfo == NULL); // If the file buffer is not allocated yet, do it. if(hf->pbFileSector == NULL) { dwErrCode = AllocateSectorBuffer(hf); if(dwErrCode != ERROR_SUCCESS || hf->pbFileSector == NULL) return dwErrCode; pbRawData = hf->pbFileSector; } // If the file sector is not loaded yet, do it if(hf->dwSectorOffs != 0) { // Is the file compressed? if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) { // Allocate space for compressed data pbCompressed = STORM_ALLOC(BYTE, pFileEntry->dwCmpSize); if(pbCompressed == NULL) return ERROR_NOT_ENOUGH_MEMORY; pbRawData = pbCompressed; } // Load the raw (compressed, encrypted) data if(!FileStream_Read(ha->pStream, &RawFilePos, pbRawData, pFileEntry->dwCmpSize)) { STORM_FREE(pbCompressed); return GetLastError(); } // If the file is encrypted, we have to decrypt the data first if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) { DecryptMpkTable(pbRawData, pFileEntry->dwCmpSize); } // If the file is compressed, we have to decompress it now if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) { int cbOutBuffer = (int)hf->dwDataSize; hf->dwCompression0 = pbRawData[0]; if(!SCompDecompressMpk(hf->pbFileSector, &cbOutBuffer, pbRawData, (int)pFileEntry->dwCmpSize)) dwErrCode = ERROR_FILE_CORRUPT; } else { if(pbRawData != hf->pbFileSector) memcpy(hf->pbFileSector, pbRawData, hf->dwDataSize); } // Free the decompression buffer. if(pbCompressed != NULL) STORM_FREE(pbCompressed); // The file sector is now properly loaded hf->dwSectorOffs = 0; } // At this moment, we have the file loaded into the file buffer. // Copy as much as the caller wants if(dwErrCode == ERROR_SUCCESS && hf->dwSectorOffs == 0) { // File position is greater or equal to file size ? if(dwFilePos >= hf->dwDataSize) { *pdwBytesRead = 0; return ERROR_SUCCESS; } // If not enough bytes remaining in the file, cut them if((hf->dwDataSize - dwFilePos) < dwToRead) dwToRead = (hf->dwDataSize - dwFilePos); // Copy the bytes memcpy(pvBuffer, hf->pbFileSector + dwFilePos, dwToRead); // Give the number of bytes read *pdwBytesRead = dwToRead; return ERROR_SUCCESS; } // An error, sorry return ERROR_CAN_NOT_COMPLETE; } static DWORD ReadMpqFileSectorFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwBytesToRead, LPDWORD pdwBytesRead) { TMPQArchive * ha = hf->ha; LPBYTE pbBuffer = (BYTE *)pvBuffer; DWORD dwTotalBytesRead = 0; // Total bytes read in all three parts DWORD dwSectorSizeMask = ha->dwSectorSize - 1; // Mask for block size, usually 0x0FFF DWORD dwFileSectorPos; // File offset of the loaded sector DWORD dwBytesRead; // Number of bytes read (temporary variable) DWORD dwErrCode; // If the file position is at or beyond end of file, do nothing if(dwFilePos >= hf->dwDataSize) { *pdwBytesRead = 0; return ERROR_SUCCESS; } // If not enough bytes in the file remaining, cut them if(dwBytesToRead > (hf->dwDataSize - dwFilePos)) dwBytesToRead = (hf->dwDataSize - dwFilePos); // Compute sector position in the file dwFileSectorPos = dwFilePos & ~dwSectorSizeMask; // Position in the block // If the file sector buffer is not allocated yet, do it now if(hf->pbFileSector == NULL) { dwErrCode = AllocateSectorBuffer(hf); if(dwErrCode != ERROR_SUCCESS || hf->pbFileSector == NULL) return dwErrCode; } // Load the first (incomplete) file sector if(dwFilePos & dwSectorSizeMask) { DWORD dwBytesInSector = ha->dwSectorSize; DWORD dwBufferOffs = dwFilePos & dwSectorSizeMask; DWORD dwToCopy; // Is the file sector already loaded ? if(hf->dwSectorOffs != dwFileSectorPos) { // Load one MPQ sector into archive buffer dwErrCode = ReadMpqSectors(hf, hf->pbFileSector, dwFileSectorPos, ha->dwSectorSize, &dwBytesInSector); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Remember that the data loaded to the sector have new file offset hf->dwSectorOffs = dwFileSectorPos; } else { if((dwFileSectorPos + dwBytesInSector) > hf->dwDataSize) dwBytesInSector = hf->dwDataSize - dwFileSectorPos; } // Copy the data from the offset in the loaded sector to the end of the sector dwToCopy = dwBytesInSector - dwBufferOffs; if(dwToCopy > dwBytesToRead) dwToCopy = dwBytesToRead; // Copy data from sector buffer into target buffer memcpy(pbBuffer, hf->pbFileSector + dwBufferOffs, dwToCopy); // Update pointers and byte counts dwTotalBytesRead += dwToCopy; dwFileSectorPos += dwBytesInSector; pbBuffer += dwToCopy; dwBytesToRead -= dwToCopy; } // Load the whole ("middle") sectors only if there is at least one full sector to be read if(dwBytesToRead >= ha->dwSectorSize) { DWORD dwBlockBytes = dwBytesToRead & ~dwSectorSizeMask; // Load all sectors to the output buffer dwErrCode = ReadMpqSectors(hf, pbBuffer, dwFileSectorPos, dwBlockBytes, &dwBytesRead); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Update pointers dwTotalBytesRead += dwBytesRead; dwFileSectorPos += dwBytesRead; pbBuffer += dwBytesRead; dwBytesToRead -= dwBytesRead; } // Read the terminating sector if(dwBytesToRead > 0) { DWORD dwToCopy = ha->dwSectorSize; // Is the file sector already loaded ? if(hf->dwSectorOffs != dwFileSectorPos) { // Load one MPQ sector into archive buffer dwErrCode = ReadMpqSectors(hf, hf->pbFileSector, dwFileSectorPos, ha->dwSectorSize, &dwBytesRead); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Remember that the data loaded to the sector have new file offset hf->dwSectorOffs = dwFileSectorPos; } // Check number of bytes read if(dwToCopy > dwBytesToRead) dwToCopy = dwBytesToRead; // Copy the data from the cached last sector to the caller's buffer memcpy(pbBuffer, hf->pbFileSector, dwToCopy); // Update pointers dwTotalBytesRead += dwToCopy; } // Store total number of bytes read to the caller *pdwBytesRead = dwTotalBytesRead; return ERROR_SUCCESS; } static DWORD ReadMpqFilePatchFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead) { TMPQPatcher Patcher; DWORD dwBytesToRead = dwToRead; DWORD dwBytesRead = 0; DWORD dwErrCode = ERROR_SUCCESS; // Make sure that the patch file is loaded completely if(dwErrCode == ERROR_SUCCESS && hf->pbFileData == NULL) { // Initialize patching process and allocate data dwErrCode = Patch_InitPatcher(&Patcher, hf); if(dwErrCode != ERROR_SUCCESS) return dwErrCode; // Set the current data size Patcher.cbFileData = hf->pFileEntry->dwFileSize; // Initialize the patcher object with initial file data if(hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) dwErrCode = ReadMpqFileSingleUnit(hf, Patcher.pbFileData1, 0, Patcher.cbFileData, &dwBytesRead); else dwErrCode = ReadMpqFileSectorFile(hf, Patcher.pbFileData1, 0, Patcher.cbFileData, &dwBytesRead); // Perform the patching process if(dwErrCode == ERROR_SUCCESS) dwErrCode = Patch_Process(&Patcher, hf); // Finalize the patcher structure Patch_Finalize(&Patcher); dwBytesRead = 0; } // If there is something to read, do it if(dwErrCode == ERROR_SUCCESS) { if(dwFilePos < hf->cbFileData) { // Make sure we don't copy more than file size if((dwFilePos + dwToRead) > hf->cbFileData) dwToRead = hf->cbFileData - dwFilePos; // Copy the appropriate amount of the file data to the caller's buffer memcpy(pvBuffer, hf->pbFileData + dwFilePos, dwToRead); dwBytesRead = dwToRead; } // Set the proper error code dwErrCode = (dwBytesRead == dwBytesToRead) ? ERROR_SUCCESS : ERROR_HANDLE_EOF; } // Give the result to the caller if(pdwBytesRead != NULL) *pdwBytesRead = dwBytesRead; return dwErrCode; } static DWORD ReadMpqFileLocalFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead) { ULONGLONG FilePosition1 = dwFilePos; ULONGLONG FilePosition2; DWORD dwBytesRead = 0; DWORD dwErrCode = ERROR_SUCCESS; assert(hf->pStream != NULL); // Because stream I/O functions are designed to read // "all or nothing", we compare file position before and after, // and if they differ, we assume that number of bytes read // is the difference between them if(!FileStream_Read(hf->pStream, &FilePosition1, pvBuffer, dwToRead)) { // If not all bytes have been read, then return the number of bytes read if((dwErrCode = GetLastError()) == ERROR_HANDLE_EOF) { FileStream_GetPos(hf->pStream, &FilePosition2); dwBytesRead = (DWORD)(FilePosition2 - FilePosition1); } } else { dwBytesRead = dwToRead; } *pdwBytesRead = dwBytesRead; return dwErrCode; } //----------------------------------------------------------------------------- // SFileReadFile bool WINAPI SFileReadFile(HANDLE hFile, void * pvBuffer, DWORD dwToRead, LPDWORD pdwRead, LPOVERLAPPED lpOverlapped) { TMPQFile * hf = (TMPQFile *)hFile; DWORD dwBytesRead = 0; // Number of bytes read DWORD dwErrCode = ERROR_SUCCESS; // Always zero the result if(pdwRead != NULL) *pdwRead = 0; lpOverlapped = lpOverlapped; // Check valid parameters if(!IsValidFileHandle(hFile)) { SetLastError(ERROR_INVALID_HANDLE); return false; } if(pvBuffer == NULL) { SetLastError(ERROR_INVALID_PARAMETER); return false; } // If we didn't load the patch info yet, do it now if(hf->pFileEntry != NULL && (hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) && hf->pPatchInfo == NULL) { dwErrCode = AllocatePatchInfo(hf, true); if(dwErrCode != ERROR_SUCCESS || hf->pPatchInfo == NULL) { SetLastError(dwErrCode); return false; } } // Clear the last used compression hf->dwCompression0 = 0; // If the file is local file, read the data directly from the stream if(hf->pStream != NULL) { dwErrCode = ReadMpqFileLocalFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); } // If the file is a patch file, we have to read it special way else if(hf->hfPatch != NULL && (hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0) { dwErrCode = ReadMpqFilePatchFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); } // If the archive is a MPK archive, we need special way to read the file else if(hf->ha->dwSubType == MPQ_SUBTYPE_MPK) { dwErrCode = ReadMpkFileSingleUnit(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); } // If the file is single unit file, redirect it to read file else if(hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT) { dwErrCode = ReadMpqFileSingleUnit(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); } // Otherwise read it as sector based MPQ file else { dwErrCode = ReadMpqFileSectorFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); } // Increment the file position hf->dwFilePos += dwBytesRead; // Give the caller the number of bytes read if(pdwRead != NULL) *pdwRead = dwBytesRead; // If the read operation succeeded, but not full number of bytes was read, // set the last error to ERROR_HANDLE_EOF if(dwErrCode == ERROR_SUCCESS && (dwBytesRead < dwToRead)) dwErrCode = ERROR_HANDLE_EOF; // If something failed, set the last error value if(dwErrCode != ERROR_SUCCESS) SetLastError(dwErrCode); return (dwErrCode == ERROR_SUCCESS); } //----------------------------------------------------------------------------- // SFileGetFileSize DWORD WINAPI SFileGetFileSize(HANDLE hFile, LPDWORD pdwFileSizeHigh) { ULONGLONG FileSize; TMPQFile * hf = (TMPQFile *)hFile; // Validate the file handle before we go on if(IsValidFileHandle(hFile)) { // Make sure that the variable is initialized FileSize = 0; // If the file is patched file, we have to get the size of the last version if(hf->hfPatch != NULL) { // Walk through the entire patch chain, take the last version while(hf != NULL) { // Get the size of the currently pointed version FileSize = hf->pFileEntry->dwFileSize; // Move to the next patch file in the hierarchy hf = hf->hfPatch; } } else { // Is it a local file ? if(hf->pStream != NULL) { FileStream_GetSize(hf->pStream, &FileSize); } else { FileSize = hf->dwDataSize; } } // If opened from archive, return file size if(pdwFileSizeHigh != NULL) *pdwFileSizeHigh = (DWORD)(FileSize >> 32); return (DWORD)FileSize; } SetLastError(ERROR_INVALID_HANDLE); return SFILE_INVALID_SIZE; } DWORD WINAPI SFileSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod) { TMPQFile * hf = (TMPQFile *)hFile; ULONGLONG OldPosition; ULONGLONG NewPosition; ULONGLONG FileSize; ULONGLONG DeltaPos; // If the hFile is not a valid file handle, return an error. if(!IsValidFileHandle(hFile)) { SetLastError(ERROR_INVALID_HANDLE); return SFILE_INVALID_POS; } // Retrieve the file size for handling the limits if(hf->pStream != NULL) { FileStream_GetSize(hf->pStream, &FileSize); } else { FileSize = SFileGetFileSize(hFile, NULL); } // Handle the NULL and non-NULL values of plFilePosHigh // Non-NULL: The DeltaPos is combined from lFilePos and *lpFilePosHigh // NULL: The DeltaPos is sign-extended value of lFilePos DeltaPos = (plFilePosHigh != NULL) ? MAKE_OFFSET64(plFilePosHigh[0], lFilePos) : (ULONGLONG)(LONGLONG)lFilePos; // Get the relative point where to move from switch(dwMoveMethod) { case FILE_BEGIN: // Move relative to the file begin. OldPosition = 0; break; case FILE_CURRENT: // Retrieve the current file position if(hf->pStream != NULL) { FileStream_GetPos(hf->pStream, &OldPosition); } else { OldPosition = hf->dwFilePos; } break; case FILE_END: // Move relative to the end of the file OldPosition = FileSize; break; default: SetLastError(ERROR_INVALID_PARAMETER); return SFILE_INVALID_POS; } // Calculate the new position NewPosition = OldPosition + DeltaPos; // If moving backward, don't allow the new position go negative if((LONGLONG)DeltaPos < 0) { if(NewPosition > FileSize) // Position is negative { SetLastError(ERROR_NEGATIVE_SEEK); return SFILE_INVALID_POS; } } // If moving forward, don't allow the new position go past the end of the file else { if(NewPosition > FileSize) NewPosition = FileSize; } // Now apply the file pointer to the file if(hf->pStream != NULL) { if(!FileStream_Read(hf->pStream, &NewPosition, NULL, 0)) return SFILE_INVALID_POS; } // Also, store the new file position to the TMPQFile struct hf->dwFilePos = (DWORD)NewPosition; // Return the new file position if(plFilePosHigh != NULL) *plFilePosHigh = (LONG)(NewPosition >> 32); return (DWORD)NewPosition; }