mirror of
https://github.com/mangosfour/server.git
synced 2025-12-16 04:37:00 +00:00
1186 lines
41 KiB
C++
1186 lines
41 KiB
C++
/*****************************************************************************/
|
|
/* 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 structures
|
|
|
|
struct TFileHeader2Ext
|
|
{
|
|
DWORD dwOffset00Data; // Required data at offset 00 (32-bits)
|
|
DWORD dwOffset00Mask; // Mask for data at offset 00 (32 bits). 0 = data are ignored
|
|
DWORD dwOffset04Data; // Required data at offset 04 (32-bits)
|
|
DWORD dwOffset04Mask; // Mask for data at offset 04 (32 bits). 0 = data are ignored
|
|
const char * szExt; // Supplied extension, if the condition is true
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Local functions
|
|
|
|
static void CopyFileName(char * szTarget, const TCHAR * szSource)
|
|
{
|
|
while(*szSource != 0)
|
|
*szTarget++ = (char)*szSource++;
|
|
*szTarget = 0;
|
|
}
|
|
|
|
static DWORD GetMpqFileCount(TMPQArchive * ha)
|
|
{
|
|
TFileEntry * pFileTableEnd;
|
|
TFileEntry * pFileEntry;
|
|
DWORD dwFileCount = 0;
|
|
|
|
// Go through all open MPQs, including patches
|
|
while(ha != NULL)
|
|
{
|
|
// Only count files that are not patch files
|
|
pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
|
|
for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
|
|
{
|
|
// If the file is patch file and this is not primary archive, skip it
|
|
// BUGBUG: This errorneously counts non-patch files that are in both
|
|
// base MPQ and in patches, and increases the number of files by cca 50%
|
|
if((pFileEntry->dwFlags & (MPQ_FILE_EXISTS | MPQ_FILE_PATCH_FILE)) == MPQ_FILE_EXISTS)
|
|
dwFileCount++;
|
|
}
|
|
|
|
// Move to the next patch archive
|
|
ha = ha->haPatch;
|
|
}
|
|
|
|
return dwFileCount;
|
|
}
|
|
|
|
static bool GetFilePatchChain(TMPQFile * hf, void * pvFileInfo, DWORD cbFileInfo, LPDWORD pcbLengthNeeded)
|
|
{
|
|
TMPQFile * hfTemp;
|
|
TCHAR * szPatchChain = (TCHAR *)pvFileInfo;
|
|
TCHAR * szFileName;
|
|
size_t cchCharsNeeded = 1;
|
|
size_t nLength;
|
|
DWORD cbLengthNeeded;
|
|
|
|
// Check if the "hf" is a MPQ file
|
|
if(hf->pStream != NULL)
|
|
{
|
|
// Calculate the length needed
|
|
szFileName = FileStream_GetFileName(hf->pStream);
|
|
cchCharsNeeded += _tcslen(szFileName) + 1;
|
|
cbLengthNeeded = (DWORD)(cchCharsNeeded * sizeof(TCHAR));
|
|
|
|
// If we have enough space, copy the file name
|
|
if(cbFileInfo >= cbLengthNeeded)
|
|
{
|
|
nLength = _tcslen(szFileName) + 1;
|
|
memcpy(szPatchChain, szFileName, nLength * sizeof(TCHAR));
|
|
szPatchChain += nLength;
|
|
|
|
// Terminate the multi-string
|
|
*szPatchChain = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// Calculate number of characters needed
|
|
for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatchFile)
|
|
cchCharsNeeded += _tcslen(FileStream_GetFileName(hfTemp->ha->pStream)) + 1;
|
|
cbLengthNeeded = (DWORD)(cchCharsNeeded * sizeof(TCHAR));
|
|
|
|
// If we have enough space, the copy the patch chain
|
|
if(cbFileInfo >= cbLengthNeeded)
|
|
{
|
|
for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatchFile)
|
|
{
|
|
szFileName = FileStream_GetFileName(hfTemp->ha->pStream);
|
|
nLength = _tcslen(szFileName) + 1;
|
|
memcpy(szPatchChain, szFileName, nLength * sizeof(TCHAR));
|
|
szPatchChain += nLength;
|
|
}
|
|
|
|
// Terminate the multi-string
|
|
*szPatchChain = 0;
|
|
}
|
|
}
|
|
|
|
// Give result length, terminate multi-string and return
|
|
*pcbLengthNeeded = cbLengthNeeded;
|
|
return true;
|
|
}
|
|
|
|
// 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 int 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;
|
|
int nError = 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_COMPRESSED)
|
|
{
|
|
// If the sector positions are not loaded yet, do it
|
|
if(hf->SectorOffsets == NULL)
|
|
{
|
|
nError = AllocateSectorOffsets(hf, true);
|
|
if(nError != ERROR_SUCCESS)
|
|
return nError;
|
|
}
|
|
|
|
// 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)
|
|
// {
|
|
// nError = AllocateRawMD5s(hf, true);
|
|
// if(nError != ERROR_SUCCESS)
|
|
// return nError;
|
|
// }
|
|
|
|
// If the file is compressed, also allocate secondary buffer
|
|
pbInSector = pbRawSector = STORM_ALLOC(BYTE, dwBytesToRead);
|
|
if(pbRawSector == NULL)
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
// Assign the temporary buffer as target for read operation
|
|
dwRawSectorOffset = hf->SectorOffsets[dwSectorIndex];
|
|
dwRawBytesToRead = hf->SectorOffsets[dwSectorIndex + dwSectorsToRead] - dwRawSectorOffset;
|
|
}
|
|
|
|
// Calculate raw file offset where the sector(s) are stored.
|
|
CalculateRawSectorOffset(RawFilePos, hf, dwRawSectorOffset);
|
|
|
|
// Set file pointer and read all required sectors
|
|
if(!FileStream_Read(ha->pStream, &RawFilePos, pbInSector, dwRawBytesToRead))
|
|
return GetLastError();
|
|
dwBytesRead = 0;
|
|
|
|
// 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_COMPRESSED)
|
|
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);
|
|
if(hf->dwFileKey == 0)
|
|
{
|
|
nError = 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)
|
|
{
|
|
nError = 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)
|
|
{
|
|
if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2)
|
|
nResult = SCompDecompress2((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector);
|
|
else
|
|
nResult = SCompDecompress((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector);
|
|
}
|
|
|
|
// Is the file compressed by PKWARE Data Compression Library ?
|
|
else if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE)
|
|
{
|
|
nResult = SCompExplode((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector);
|
|
}
|
|
|
|
// Did the decompression fail ?
|
|
if(nResult == 0)
|
|
{
|
|
nError = 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++;
|
|
}
|
|
|
|
// Free all used buffers
|
|
if(pbRawSector != NULL)
|
|
STORM_FREE(pbRawSector);
|
|
|
|
// Give the caller thenumber of bytes read
|
|
*pdwBytesRead = dwBytesRead;
|
|
return nError;
|
|
}
|
|
|
|
static int 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 = NULL;
|
|
int nError = ERROR_SUCCESS;
|
|
|
|
// If the file buffer is not allocated yet, do it.
|
|
if(hf->pbFileSector == NULL)
|
|
{
|
|
nError = AllocateSectorBuffer(hf);
|
|
if(nError != ERROR_SUCCESS)
|
|
return nError;
|
|
pbRawData = hf->pbFileSector;
|
|
}
|
|
|
|
// If the file is a patch file, adjust raw data offset
|
|
if(hf->pPatchInfo != NULL)
|
|
RawFilePos += hf->pPatchInfo->dwLength;
|
|
|
|
// If the file sector is not loaded yet, do it
|
|
if(hf->dwSectorOffs != 0)
|
|
{
|
|
// Is the file compressed?
|
|
if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED)
|
|
{
|
|
// 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_COMPRESSED)
|
|
{
|
|
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)
|
|
{
|
|
if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2)
|
|
nResult = SCompDecompress2((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, cbInBuffer);
|
|
else
|
|
nResult = SCompDecompress((char *)hf->pbFileSector, &cbOutBuffer, (char *)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((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, cbInBuffer);
|
|
|
|
nError = (nResult != 0) ? ERROR_SUCCESS : 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(nError == 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 int ReadMpqFile(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)
|
|
int nError;
|
|
|
|
// 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)
|
|
{
|
|
nError = AllocateSectorBuffer(hf);
|
|
if(nError != ERROR_SUCCESS)
|
|
return nError;
|
|
}
|
|
|
|
// 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
|
|
nError = ReadMpqSectors(hf, hf->pbFileSector, dwFileSectorPos, ha->dwSectorSize, &dwBytesInSector);
|
|
if(nError != ERROR_SUCCESS)
|
|
return nError;
|
|
|
|
// 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
|
|
nError = ReadMpqSectors(hf, pbBuffer, dwFileSectorPos, dwBlockBytes, &dwBytesRead);
|
|
if(nError != ERROR_SUCCESS)
|
|
return nError;
|
|
|
|
// 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
|
|
nError = ReadMpqSectors(hf, hf->pbFileSector, dwFileSectorPos, ha->dwSectorSize, &dwBytesRead);
|
|
if(nError != ERROR_SUCCESS)
|
|
return nError;
|
|
|
|
// 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 int ReadMpqFilePatchFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead)
|
|
{
|
|
DWORD dwBytesToRead = dwToRead;
|
|
DWORD dwBytesRead = 0;
|
|
int nError = ERROR_SUCCESS;
|
|
|
|
// Make sure that the patch file is loaded completely
|
|
if(hf->pbFileData == NULL)
|
|
{
|
|
// Load the original file and store its content to "pbOldData"
|
|
hf->pbFileData = STORM_ALLOC(BYTE, hf->pFileEntry->dwFileSize);
|
|
hf->cbFileData = hf->pFileEntry->dwFileSize;
|
|
if(hf->pbFileData == NULL)
|
|
return ERROR_NOT_ENOUGH_MEMORY;
|
|
|
|
// Read the file data
|
|
if(hf->pFileEntry->dwFlags & MPQ_FILE_SINGLE_UNIT)
|
|
nError = ReadMpqFileSingleUnit(hf, hf->pbFileData, 0, hf->cbFileData, &dwBytesRead);
|
|
else
|
|
nError = ReadMpqFile(hf, hf->pbFileData, 0, hf->cbFileData, &dwBytesRead);
|
|
|
|
// Fix error code
|
|
if(nError == ERROR_SUCCESS && dwBytesRead != hf->cbFileData)
|
|
nError = ERROR_FILE_CORRUPT;
|
|
|
|
// Patch the file data
|
|
if(nError == ERROR_SUCCESS)
|
|
nError = PatchFileData(hf);
|
|
|
|
// Reset number of bytes read to zero
|
|
dwBytesRead = 0;
|
|
}
|
|
|
|
// If there is something to read, do it
|
|
if(nError == 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
|
|
nError = (dwBytesRead == dwBytesToRead) ? ERROR_SUCCESS : ERROR_HANDLE_EOF;
|
|
}
|
|
|
|
// Give the result to the caller
|
|
if(pdwBytesRead != NULL)
|
|
*pdwBytesRead = dwBytesRead;
|
|
return nError;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// 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
|
|
int nError = ERROR_SUCCESS;
|
|
|
|
// Keep compilers happy
|
|
lpOverlapped = lpOverlapped;
|
|
|
|
// Check valid parameters
|
|
if(!IsValidFileHandle(hf))
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return false;
|
|
}
|
|
|
|
if(pvBuffer == NULL)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return false;
|
|
}
|
|
|
|
// If the file is local file, read the data directly from the stream
|
|
if(hf->pStream != NULL)
|
|
{
|
|
ULONGLONG FilePosition1;
|
|
ULONGLONG FilePosition2;
|
|
|
|
// 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
|
|
|
|
FileStream_GetPos(hf->pStream, &FilePosition1);
|
|
if(!FileStream_Read(hf->pStream, NULL, pvBuffer, dwToRead))
|
|
{
|
|
// If not all bytes have been read, then return the number
|
|
// of bytes read
|
|
if((nError = GetLastError()) == ERROR_HANDLE_EOF)
|
|
{
|
|
FileStream_GetPos(hf->pStream, &FilePosition2);
|
|
dwBytesRead = (DWORD)(FilePosition2 - FilePosition1);
|
|
}
|
|
else
|
|
{
|
|
nError = GetLastError();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
dwBytesRead = dwToRead;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If the file is a patch file, we have to read it special way
|
|
if(hf->hfPatchFile != NULL && (hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0)
|
|
{
|
|
nError = ReadMpqFilePatchFile(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)
|
|
{
|
|
nError = ReadMpqFileSingleUnit(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead);
|
|
}
|
|
|
|
// Otherwise read it as sector based MPQ file
|
|
else
|
|
{
|
|
nError = ReadMpqFile(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(nError == ERROR_SUCCESS && (dwBytesRead < dwToRead))
|
|
nError = ERROR_HANDLE_EOF;
|
|
|
|
// If something failed, set the last error value
|
|
if(nError != ERROR_SUCCESS)
|
|
SetLastError(nError);
|
|
return (nError == 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(hf))
|
|
{
|
|
// 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->hfPatchFile != 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->hfPatchFile;
|
|
}
|
|
}
|
|
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 FilePosition;
|
|
ULONGLONG MoveOffset;
|
|
DWORD dwFilePosHi;
|
|
|
|
// If the hFile is not a valid file handle, return an error.
|
|
if(!IsValidFileHandle(hf))
|
|
{
|
|
SetLastError(ERROR_INVALID_HANDLE);
|
|
return SFILE_INVALID_POS;
|
|
}
|
|
|
|
// Get the relative point where to move from
|
|
switch(dwMoveMethod)
|
|
{
|
|
case FILE_BEGIN:
|
|
FilePosition = 0;
|
|
break;
|
|
|
|
case FILE_CURRENT:
|
|
if(hf->pStream != NULL)
|
|
{
|
|
FileStream_GetPos(hf->pStream, &FilePosition);
|
|
}
|
|
else
|
|
{
|
|
FilePosition = hf->dwFilePos;
|
|
}
|
|
break;
|
|
|
|
case FILE_END:
|
|
if(hf->pStream != NULL)
|
|
{
|
|
FileStream_GetSize(hf->pStream, &FilePosition);
|
|
}
|
|
else
|
|
{
|
|
FilePosition = SFileGetFileSize(hFile, NULL);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return SFILE_INVALID_POS;
|
|
}
|
|
|
|
// Now get the move offset. Note that both values form
|
|
// a signed 64-bit value (a file pointer can be moved backwards)
|
|
if(plFilePosHigh != NULL)
|
|
dwFilePosHi = *plFilePosHigh;
|
|
else
|
|
dwFilePosHi = (lFilePos & 0x80000000) ? 0xFFFFFFFF : 0;
|
|
MoveOffset = MAKE_OFFSET64(dwFilePosHi, lFilePos);
|
|
|
|
// Now calculate the new file pointer
|
|
// Do not allow the file pointer to go before the begin of the file
|
|
FilePosition += MoveOffset;
|
|
if(FilePosition < 0)
|
|
FilePosition = 0;
|
|
|
|
// Now apply the file pointer to the file
|
|
if(hf->pStream != NULL)
|
|
{
|
|
// Apply the new file position
|
|
if(!FileStream_Read(hf->pStream, &FilePosition, NULL, 0))
|
|
return SFILE_INVALID_POS;
|
|
|
|
// Return the new file position
|
|
if(plFilePosHigh != NULL)
|
|
*plFilePosHigh = (LONG)(FilePosition >> 32);
|
|
return (DWORD)FilePosition;
|
|
}
|
|
else
|
|
{
|
|
// Files in MPQ can't be bigger than 4 GB.
|
|
// We don't allow to go past 4 GB
|
|
if(FilePosition >> 32)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return SFILE_INVALID_POS;
|
|
}
|
|
|
|
// Change the file position
|
|
hf->dwFilePos = (DWORD)FilePosition;
|
|
|
|
// Return the new file position
|
|
if(plFilePosHigh != NULL)
|
|
*plFilePosHigh = 0;
|
|
return (DWORD)FilePosition;
|
|
}
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Tries to retrieve the file name
|
|
|
|
static TFileHeader2Ext data2ext[] =
|
|
{
|
|
{0x00005A4D, 0x0000FFFF, 0x00000000, 0x00000000, "exe"}, // EXE files
|
|
{0x00000006, 0xFFFFFFFF, 0x00000001, 0xFFFFFFFF, "dc6"}, // EXE files
|
|
{0x1A51504D, 0xFFFFFFFF, 0x00000000, 0x00000000, "mpq"}, // MPQ archive header ID ('MPQ\x1A')
|
|
{0x46464952, 0xFFFFFFFF, 0x00000000, 0x00000000, "wav"}, // WAVE header 'RIFF'
|
|
{0x324B4D53, 0xFFFFFFFF, 0x00000000, 0x00000000, "smk"}, // Old "Smacker Video" files 'SMK2'
|
|
{0x694B4942, 0xFFFFFFFF, 0x00000000, 0x00000000, "bik"}, // Bink video files (new)
|
|
{0x0801050A, 0xFFFFFFFF, 0x00000000, 0x00000000, "pcx"}, // PCX images used in Diablo I
|
|
{0x544E4F46, 0xFFFFFFFF, 0x00000000, 0x00000000, "fnt"}, // Font files used in Diablo II
|
|
{0x6D74683C, 0xFFFFFFFF, 0x00000000, 0x00000000, "html"}, // HTML '<htm'
|
|
{0x4D54483C, 0xFFFFFFFF, 0x00000000, 0x00000000, "html"}, // HTML '<HTM
|
|
{0x216F6F57, 0xFFFFFFFF, 0x00000000, 0x00000000, "tbl"}, // Table files
|
|
{0x31504C42, 0xFFFFFFFF, 0x00000000, 0x00000000, "blp"}, // BLP textures
|
|
{0x32504C42, 0xFFFFFFFF, 0x00000000, 0x00000000, "blp"}, // BLP textures (v2)
|
|
{0x584C444D, 0xFFFFFFFF, 0x00000000, 0x00000000, "mdx"}, // MDX files
|
|
{0x45505954, 0xFFFFFFFF, 0x00000000, 0x00000000, "pud"}, // Warcraft II maps
|
|
{0x38464947, 0xFFFFFFFF, 0x00000000, 0x00000000, "gif"}, // GIF images 'GIF8'
|
|
{0x3032444D, 0xFFFFFFFF, 0x00000000, 0x00000000, "m2"}, // WoW ??? .m2
|
|
{0x43424457, 0xFFFFFFFF, 0x00000000, 0x00000000, "dbc"}, // ??? .dbc
|
|
{0x47585053, 0xFFFFFFFF, 0x00000000, 0x00000000, "bls"}, // WoW pixel shaders
|
|
{0xE0FFD8FF, 0xFFFFFFFF, 0x00000000, 0x00000000, "jpg"}, // JPEG image
|
|
{0x00000000, 0x00000000, 0x00000000, 0x00000000, "xxx"}, // Default extension
|
|
{0, 0, 0, 0, NULL} // Terminator
|
|
};
|
|
|
|
bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName)
|
|
{
|
|
TFileEntry * pFileEntry;
|
|
TMPQFile * hf = (TMPQFile *)hFile; // MPQ File handle
|
|
char szPseudoName[20];
|
|
DWORD FirstBytes[2]; // The first 4 bytes of the file
|
|
DWORD dwFilePos; // Saved file position
|
|
int nError = ERROR_SUCCESS;
|
|
int i;
|
|
|
|
// Pre-zero the output buffer
|
|
if(szFileName != NULL)
|
|
*szFileName = 0;
|
|
|
|
// Check valid parameters
|
|
if(!IsValidFileHandle(hf))
|
|
nError = ERROR_INVALID_HANDLE;
|
|
pFileEntry = hf->pFileEntry;
|
|
|
|
// Only do something if the file name is not filled
|
|
if(nError == ERROR_SUCCESS && pFileEntry != NULL && pFileEntry->szFileName == NULL)
|
|
{
|
|
// Read the first 2 DWORDs bytes from the file
|
|
FirstBytes[0] = FirstBytes[1] = 0;
|
|
dwFilePos = SFileSetFilePointer(hf, 0, NULL, FILE_CURRENT);
|
|
SFileReadFile(hFile, FirstBytes, sizeof(FirstBytes), NULL, NULL);
|
|
BSWAP_ARRAY32_UNSIGNED(FirstBytes, sizeof(FirstBytes));
|
|
SFileSetFilePointer(hf, dwFilePos, NULL, FILE_BEGIN);
|
|
|
|
// Try to guess file extension from those 2 DWORDs
|
|
for(i = 0; data2ext[i].szExt != NULL; i++)
|
|
{
|
|
if((FirstBytes[0] & data2ext[i].dwOffset00Mask) == data2ext[i].dwOffset00Data &&
|
|
(FirstBytes[1] & data2ext[i].dwOffset04Mask) == data2ext[i].dwOffset04Data)
|
|
{
|
|
sprintf(szPseudoName, "File%08u.%s", (unsigned int)(pFileEntry - hf->ha->pFileTable), data2ext[i].szExt);
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Put the file name to the file table
|
|
AllocateFileName(pFileEntry, szPseudoName);
|
|
}
|
|
|
|
// Now put the file name to the file structure
|
|
if(nError == ERROR_SUCCESS && szFileName != NULL)
|
|
{
|
|
if(pFileEntry != NULL && pFileEntry->szFileName != NULL)
|
|
strcpy(szFileName, pFileEntry->szFileName);
|
|
else if(hf->pStream != NULL)
|
|
CopyFileName(szFileName, FileStream_GetFileName(hf->pStream));
|
|
}
|
|
return (nError == ERROR_SUCCESS);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Retrieves an information about an archive or about a file within the archive
|
|
//
|
|
// hMpqOrFile - Handle to an MPQ archive or to a file
|
|
// dwInfoType - Information to obtain
|
|
|
|
#define VERIFY_MPQ_HANDLE(h) \
|
|
if(!IsValidMpqHandle(h)) \
|
|
{ \
|
|
nError = ERROR_INVALID_HANDLE; \
|
|
break; \
|
|
}
|
|
|
|
#define VERIFY_FILE_HANDLE(h) \
|
|
if(!IsValidFileHandle(h)) \
|
|
{ \
|
|
nError = ERROR_INVALID_HANDLE; \
|
|
break; \
|
|
}
|
|
|
|
bool WINAPI SFileGetFileInfo(
|
|
HANDLE hMpqOrFile,
|
|
DWORD dwInfoType,
|
|
void * pvFileInfo,
|
|
DWORD cbFileInfo,
|
|
LPDWORD pcbLengthNeeded)
|
|
{
|
|
TMPQArchive * ha = (TMPQArchive *)hMpqOrFile;
|
|
TMPQBlock * pBlock;
|
|
TMPQFile * hf = (TMPQFile *)hMpqOrFile;
|
|
void * pvSrcFileInfo = NULL;
|
|
DWORD cbLengthNeeded = 0;
|
|
DWORD dwIsReadOnly;
|
|
DWORD dwFileCount = 0;
|
|
DWORD dwFileIndex;
|
|
DWORD dwFileKey;
|
|
DWORD i;
|
|
int nError = ERROR_SUCCESS;
|
|
|
|
switch(dwInfoType)
|
|
{
|
|
case SFILE_INFO_ARCHIVE_NAME:
|
|
VERIFY_MPQ_HANDLE(ha);
|
|
|
|
// pvFileInfo receives the name of the archive, terminated by 0
|
|
pvSrcFileInfo = FileStream_GetFileName(ha->pStream);
|
|
cbLengthNeeded = (DWORD)(_tcslen((TCHAR *)pvSrcFileInfo) + 1) * sizeof(TCHAR);
|
|
break;
|
|
|
|
case SFILE_INFO_ARCHIVE_SIZE: // Size of the archive
|
|
VERIFY_MPQ_HANDLE(ha);
|
|
cbLengthNeeded = sizeof(DWORD);
|
|
pvSrcFileInfo = &ha->pHeader->dwArchiveSize;
|
|
break;
|
|
|
|
case SFILE_INFO_MAX_FILE_COUNT: // Max. number of files in the MPQ
|
|
VERIFY_MPQ_HANDLE(ha);
|
|
cbLengthNeeded = sizeof(DWORD);
|
|
pvSrcFileInfo = &ha->dwMaxFileCount;
|
|
break;
|
|
|
|
case SFILE_INFO_HASH_TABLE_SIZE: // Size of the hash table
|
|
VERIFY_MPQ_HANDLE(ha);
|
|
cbLengthNeeded = sizeof(DWORD);
|
|
pvSrcFileInfo = &ha->pHeader->dwHashTableSize;
|
|
break;
|
|
|
|
case SFILE_INFO_BLOCK_TABLE_SIZE: // Size of the block table
|
|
VERIFY_MPQ_HANDLE(ha);
|
|
cbLengthNeeded = sizeof(DWORD);
|
|
pvSrcFileInfo = &ha->pHeader->dwBlockTableSize;
|
|
break;
|
|
|
|
case SFILE_INFO_SECTOR_SIZE:
|
|
VERIFY_MPQ_HANDLE(ha);
|
|
cbLengthNeeded = sizeof(DWORD);
|
|
pvSrcFileInfo = &ha->dwSectorSize;
|
|
break;
|
|
|
|
case SFILE_INFO_HASH_TABLE:
|
|
VERIFY_MPQ_HANDLE(ha);
|
|
cbLengthNeeded = ha->pHeader->dwHashTableSize * sizeof(TMPQHash);
|
|
pvSrcFileInfo = ha->pHashTable;
|
|
break;
|
|
|
|
case SFILE_INFO_BLOCK_TABLE:
|
|
VERIFY_MPQ_HANDLE(ha);
|
|
cbLengthNeeded = ha->dwFileTableSize * sizeof(TMPQBlock);
|
|
if(cbFileInfo < cbLengthNeeded)
|
|
{
|
|
nError = ERROR_INSUFFICIENT_BUFFER;
|
|
break;
|
|
}
|
|
|
|
// Construct block table from file table size
|
|
pBlock = (TMPQBlock *)pvFileInfo;
|
|
for(i = 0; i < ha->dwFileTableSize; i++)
|
|
{
|
|
pBlock->dwFilePos = (DWORD)ha->pFileTable[i].ByteOffset;
|
|
pBlock->dwFSize = ha->pFileTable[i].dwFileSize;
|
|
pBlock->dwCSize = ha->pFileTable[i].dwCmpSize;
|
|
pBlock->dwFlags = ha->pFileTable[i].dwFlags;
|
|
pBlock++;
|
|
}
|
|
break;
|
|
|
|
case SFILE_INFO_NUM_FILES:
|
|
VERIFY_MPQ_HANDLE(ha);
|
|
dwFileCount = GetMpqFileCount(ha);
|
|
cbLengthNeeded = sizeof(DWORD);
|
|
pvSrcFileInfo = &dwFileCount;
|
|
break;
|
|
|
|
case SFILE_INFO_STREAM_FLAGS:
|
|
VERIFY_MPQ_HANDLE(ha);
|
|
FileStream_GetFlags(ha->pStream, &dwFileKey);
|
|
cbLengthNeeded = sizeof(DWORD);
|
|
pvSrcFileInfo = &dwFileKey;
|
|
break;
|
|
|
|
case SFILE_INFO_IS_READ_ONLY:
|
|
VERIFY_MPQ_HANDLE(ha);
|
|
dwIsReadOnly = (FileStream_IsReadOnly(ha->pStream) || (ha->dwFlags & MPQ_FLAG_READ_ONLY));
|
|
cbLengthNeeded = sizeof(DWORD);
|
|
pvSrcFileInfo = &dwIsReadOnly;
|
|
break;
|
|
|
|
case SFILE_INFO_HASH_INDEX:
|
|
VERIFY_FILE_HANDLE(hf);
|
|
cbLengthNeeded = sizeof(DWORD);
|
|
pvSrcFileInfo = &hf->pFileEntry->dwHashIndex;
|
|
break;
|
|
|
|
case SFILE_INFO_CODENAME1:
|
|
VERIFY_FILE_HANDLE(hf);
|
|
cbLengthNeeded = sizeof(DWORD);
|
|
pvSrcFileInfo = &hf->pFileEntry->dwHashIndex;
|
|
if(ha->pHashTable != NULL)
|
|
pvSrcFileInfo = &ha->pHashTable[hf->pFileEntry->dwHashIndex].dwName1;
|
|
break;
|
|
|
|
case SFILE_INFO_CODENAME2:
|
|
VERIFY_FILE_HANDLE(hf);
|
|
cbLengthNeeded = sizeof(DWORD);
|
|
if(ha->pHashTable != NULL)
|
|
pvSrcFileInfo = &ha->pHashTable[hf->pFileEntry->dwHashIndex].dwName2;
|
|
break;
|
|
|
|
case SFILE_INFO_LOCALEID:
|
|
VERIFY_FILE_HANDLE(hf);
|
|
cbLengthNeeded = sizeof(DWORD);
|
|
pvSrcFileInfo = &hf->pFileEntry->lcLocale;
|
|
break;
|
|
|
|
case SFILE_INFO_BLOCKINDEX:
|
|
VERIFY_FILE_HANDLE(hf);
|
|
dwFileIndex = (DWORD)(hf->pFileEntry - hf->ha->pFileTable);
|
|
cbLengthNeeded = sizeof(DWORD);
|
|
pvSrcFileInfo = &dwFileIndex;
|
|
break;
|
|
|
|
case SFILE_INFO_FILE_SIZE:
|
|
VERIFY_FILE_HANDLE(hf);
|
|
cbLengthNeeded = sizeof(DWORD);
|
|
pvSrcFileInfo = &hf->pFileEntry->dwFileSize;
|
|
break;
|
|
|
|
case SFILE_INFO_COMPRESSED_SIZE:
|
|
VERIFY_FILE_HANDLE(hf);
|
|
cbLengthNeeded = sizeof(DWORD);
|
|
pvSrcFileInfo = &hf->pFileEntry->dwCmpSize;
|
|
break;
|
|
|
|
case SFILE_INFO_FLAGS:
|
|
VERIFY_FILE_HANDLE(hf);
|
|
cbLengthNeeded = sizeof(DWORD);
|
|
pvSrcFileInfo = &hf->pFileEntry->dwFlags;
|
|
break;
|
|
|
|
case SFILE_INFO_POSITION:
|
|
VERIFY_FILE_HANDLE(hf);
|
|
cbLengthNeeded = sizeof(ULONGLONG);
|
|
pvSrcFileInfo = &hf->pFileEntry->ByteOffset;
|
|
break;
|
|
|
|
case SFILE_INFO_KEY:
|
|
VERIFY_FILE_HANDLE(hf);
|
|
cbLengthNeeded = sizeof(DWORD);
|
|
pvSrcFileInfo = &hf->dwFileKey;
|
|
break;
|
|
|
|
case SFILE_INFO_KEY_UNFIXED:
|
|
VERIFY_FILE_HANDLE(hf);
|
|
dwFileKey = hf->dwFileKey;
|
|
if(hf->pFileEntry->dwFlags & MPQ_FILE_FIX_KEY)
|
|
dwFileKey = (dwFileKey ^ hf->pFileEntry->dwFileSize) - (DWORD)hf->MpqFilePos;
|
|
cbLengthNeeded = sizeof(DWORD);
|
|
pvSrcFileInfo = &dwFileKey;
|
|
break;
|
|
|
|
case SFILE_INFO_FILETIME:
|
|
VERIFY_FILE_HANDLE(hf);
|
|
cbLengthNeeded = sizeof(ULONGLONG);
|
|
pvSrcFileInfo = &hf->pFileEntry->FileTime;
|
|
break;
|
|
|
|
case SFILE_INFO_PATCH_CHAIN:
|
|
VERIFY_FILE_HANDLE(hf);
|
|
GetFilePatchChain(hf, pvFileInfo, cbFileInfo, &cbLengthNeeded);
|
|
break;
|
|
|
|
default:
|
|
nError = ERROR_INVALID_PARAMETER;
|
|
break;
|
|
}
|
|
|
|
// If everything is OK so far, copy the information
|
|
if(nError == ERROR_SUCCESS)
|
|
{
|
|
// Is the output buffer large enough?
|
|
if(cbFileInfo >= cbLengthNeeded)
|
|
{
|
|
// Copy the data
|
|
if(pvSrcFileInfo != NULL)
|
|
memcpy(pvFileInfo, pvSrcFileInfo, cbLengthNeeded);
|
|
}
|
|
else
|
|
{
|
|
nError = ERROR_INSUFFICIENT_BUFFER;
|
|
}
|
|
|
|
// Give the size to the caller
|
|
if(pcbLengthNeeded != NULL)
|
|
*pcbLengthNeeded = cbLengthNeeded;
|
|
}
|
|
|
|
// Set the last error value, if needed
|
|
if(nError != ERROR_SUCCESS)
|
|
SetLastError(nError);
|
|
return (nError == ERROR_SUCCESS);
|
|
}
|