diff options
| author | KiritoDv <kiritodev01@gmail.com> | 2023-08-17 00:35:58 -0600 |
|---|---|---|
| committer | KiritoDv <kiritodev01@gmail.com> | 2023-08-17 00:35:58 -0600 |
| commit | 6fd8e5ee43dd0f87eb4e57b86e84770fcd95db25 (patch) | |
| tree | 6a22e06c6105b63a0ae560f571f6d4e2522ba364 /lib/StormLib/src/SFileReadFile.cpp | |
Added StormLib, nlohmann, BinaryTools and CLI11
Diffstat (limited to 'lib/StormLib/src/SFileReadFile.cpp')
| -rw-r--r-- | lib/StormLib/src/SFileReadFile.cpp | 913 |
1 files changed, 913 insertions, 0 deletions
diff --git a/lib/StormLib/src/SFileReadFile.cpp b/lib/StormLib/src/SFileReadFile.cpp new file mode 100644 index 0000000..8031ba7 --- /dev/null +++ b/lib/StormLib/src/SFileReadFile.cpp @@ -0,0 +1,913 @@ +/*****************************************************************************/ +/* 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) + { + if(dwRawBytesInThisSector != 0) + { + 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 + { + if(ha->dwFlags & MPQ_FLAG_STARCRAFT_BETA) + nResult = SCompDecompress_SC1B(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 + { + memset(pbOutSector, 0, dwBytesInThisSector); + } + } + 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) +{ + TFileEntry * pFileEntry; + TMPQFile * hf; + 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((hf = IsValidFileHandle(hFile)) == NULL) + { + 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 + pFileEntry = hf->pFileEntry; + 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 && (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(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; +} |
