diff options
Diffstat (limited to 'src/SFileGetFileInfo.cpp')
-rw-r--r-- | src/SFileGetFileInfo.cpp | 1698 |
1 files changed, 664 insertions, 1034 deletions
diff --git a/src/SFileGetFileInfo.cpp b/src/SFileGetFileInfo.cpp index 6a85bd9..06c8d6a 100644 --- a/src/SFileGetFileInfo.cpp +++ b/src/SFileGetFileInfo.cpp @@ -1,12 +1,11 @@ /*****************************************************************************/ -/* SFileReadFile.cpp Copyright (c) Ladislav Zezula 2003 */ +/* SFileGetFileInfo.cpp Copyright (c) Ladislav Zezula 2013 */ /*---------------------------------------------------------------------------*/ -/* Description : */ +/* 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 */ +/* 30.11.13 1.00 Lad The first version of SFileGetFileInfo.cpp */ /*****************************************************************************/ #define __STORMLIB_SELF__ @@ -14,8 +13,34 @@ #include "StormCommon.h" //----------------------------------------------------------------------------- +// Local defines + +// Information types for SFileGetFileInfo +#define SFILE_INFO_TYPE_UNKNOWN 0 +#define SFILE_INFO_TYPE_DIRECT_POINTER 1 +#define SFILE_INFO_TYPE_ALLOCATED 2 +#define SFILE_INFO_TYPE_READ_FROM_FILE 3 +#define SFILE_INFO_TYPE_TABLE_POINTER 4 +#define SFILE_INFO_TYPE_FILE_ENTRY 5 + +//----------------------------------------------------------------------------- // Local functions +static void ConvertFileEntryToSelfRelative(TFileEntry * pFileEntry, TFileEntry * pSrcFileEntry) +{ + // Copy the file entry itself + memcpy(pFileEntry, pSrcFileEntry, sizeof(TFileEntry)); + + // If source is NULL, leave it NULL + if(pSrcFileEntry->szFileName != NULL) + { + // Set the file name pointer after the file entry + pFileEntry->szFileName = (char *)(pFileEntry + 1); + strcpy(pFileEntry->szFileName, pSrcFileEntry->szFileName); + } +} + + static DWORD GetMpqFileCount(TMPQArchive * ha) { TFileEntry * pFileTableEnd; @@ -43,1245 +68,680 @@ static DWORD GetMpqFileCount(TMPQArchive * ha) return dwFileCount; } -static TCHAR * GetFilePatchChain(TMPQFile * hf, DWORD * pcbChainLength) +static bool GetFilePatchChain(TMPQFile * hf, void * pvFileInfo, DWORD cbFileInfo, DWORD * pcbLengthNeeded) { TMPQFile * hfTemp; - TCHAR * szPatchChain = NULL; - TCHAR * szPatchItem = NULL; - TCHAR * szFileName; + TCHAR * szFileInfo = (TCHAR *)pvFileInfo; size_t cchCharsNeeded = 1; + size_t cchFileInfo = (cbFileInfo / sizeof(TCHAR)); size_t nLength; // Patch chain is only supported on MPQ files. - if(hf->pStream == NULL) + if(hf->pStream != NULL) { - // Calculate the necessary length of the multi-string - for(hfTemp = hf; hfTemp != NULL; hfTemp->hfPatchFile) - cchCharsNeeded += _tcslen(FileStream_GetFileName(hfTemp->ha->pStream)) + 1; - - // Allocate space for the multi-string - szPatchChain = szPatchItem = STORM_ALLOC(TCHAR, cchCharsNeeded); - if(szPatchChain != NULL) - { - // Fill-in all the names - for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatchFile) - { - szFileName = FileStream_GetFileName(hfTemp->ha->pStream); - nLength = _tcslen(szFileName) + 1; - - memcpy(szPatchItem, szFileName, nLength * sizeof(TCHAR)); - szPatchItem += nLength; - } - - // Terminate the multi-string - *szPatchItem++ = 0; - } - - // The length must match - assert((size_t)(szPatchItem - szPatchChain) == cchCharsNeeded); + SetLastError(ERROR_INVALID_PARAMETER); + return false; } - // Give the length of the patch chain, in bytes - if(pcbChainLength != NULL) - pcbChainLength[0] = (DWORD)(cchCharsNeeded * sizeof(TCHAR)); - return szPatchChain; -} - -// 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 + // Calculate the necessary length of the multi-string + for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatch) + cchCharsNeeded += _tcslen(FileStream_GetFileName(hfTemp->ha->pStream)) + 1; - // If there is not enough bytes remaining, cut dwBytesToRead - if((dwByteOffset + dwBytesToRead) > hf->dwDataSize) - dwBytesToRead = hf->dwDataSize - dwByteOffset; - dwRawBytesToRead = dwBytesToRead; + // Give the caller the needed length + if(pcbLengthNeeded != NULL) + pcbLengthNeeded[0] = (DWORD)(cchCharsNeeded * sizeof(TCHAR)); - // Perform all necessary work to do with compressed files - if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK) + // If the caller gave both buffer pointer and data length, + // try to copy the patch chain + if(szFileInfo != NULL && cchFileInfo != 0) { - // If the sector positions are not loaded yet, do it - if(hf->SectorOffsets == NULL) + // If there is enough space in the buffer, copy the patch chain + if(cchCharsNeeded > cchFileInfo) { - nError = AllocateSectorOffsets(hf, true); - if(nError != ERROR_SUCCESS) - return nError; + SetLastError(ERROR_INSUFFICIENT_BUFFER); + return false; } - // If the sector checksums are not loaded yet, load them now. - if(hf->SectorChksums == NULL && (pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC) && hf->bLoadedSectorCRCs == false) + // Copy each patch + for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatch) { - // - // 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; + // Get the file name and its length + const TCHAR * szFileName = FileStream_GetFileName(hfTemp->ha->pStream); + nLength = _tcslen(szFileName) + 1; + + // Copy the file name + memcpy(szFileInfo, szFileName, nLength * sizeof(TCHAR)); + szFileInfo += nLength; } - // 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; + // Make it multi-string + szFileInfo[0] = 0; } - // Calculate raw file offset where the sector(s) are stored. - CalculateRawSectorOffset(RawFilePos, hf, dwRawSectorOffset); + return true; +} - // Set file pointer and read all required sectors - if(!FileStream_Read(ha->pStream, &RawFilePos, pbInSector, dwRawBytesToRead)) - return GetLastError(); - dwBytesRead = 0; +//----------------------------------------------------------------------------- +// Retrieves an information about an archive or about a file within the archive +// +// hMpqOrFile - Handle to an MPQ archive or to a file +// InfoClass - Information to obtain +// pvFileInfo - Pointer to buffer to store the information +// cbFileInfo - Size of the buffer pointed by pvFileInfo +// pcbLengthNeeded - Receives number of bytes necessary to store the information - // 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); +bool WINAPI SFileGetFileInfo( + HANDLE hMpqOrFile, + SFileInfoClass InfoClass, + void * pvFileInfo, + DWORD cbFileInfo, + LPDWORD pcbLengthNeeded) +{ + MPQ_SIGNATURE_INFO SignatureInfo; + TMPQArchive * ha = NULL; + TFileEntry * pFileEntry = NULL; + ULONGLONG Int64Value = 0; + ULONGLONG ByteOffset = 0; + TMPQFile * hf = NULL; + void * pvSrcFileInfo = NULL; + DWORD cbSrcFileInfo = 0; + DWORD dwInt32Value = 0; + int nInfoType = SFILE_INFO_TYPE_UNKNOWN; + int nError = ERROR_INVALID_PARAMETER; - // If we don't know the key, try to detect it by file content - if(hf->dwFileKey == 0) + switch(InfoClass) + { + case SFileMpqFileName: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL) { - hf->dwFileKey = DetectFileKeyByContent(pbInSector, dwBytesInThisSector); - if(hf->dwFileKey == 0) - { - nError = ERROR_UNKNOWN_FILE_KEY; - break; - } + pvSrcFileInfo = (void *)FileStream_GetFileName(ha->pStream); + cbSrcFileInfo = (DWORD)(_tcslen((TCHAR *)pvSrcFileInfo) + 1) * sizeof(TCHAR); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } + 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) + case SFileMpqUserDataOffset: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL && ha->pUserData != NULL) { - dwAdlerValue = adler32(0, pbInSector, dwRawBytesInThisSector); - if(dwAdlerValue != dwAdlerExpected) - { - nError = ERROR_CHECKSUM_ERROR; - break; - } + pvSrcFileInfo = &ha->UserDataPos; + cbSrcFileInfo = sizeof(ULONGLONG); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } - } - - // 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; + break; - // Is the file compressed by Blizzard's multiple compression ? - if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS) + case SFileMpqUserDataHeader: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL && ha->pUserData != NULL) { - if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2) - nResult = SCompDecompress2(pbOutSector, &cbOutSector, pbInSector, cbInSector); - else - nResult = SCompDecompress(pbOutSector, &cbOutSector, pbInSector, cbInSector); + ByteOffset = ha->UserDataPos; + cbSrcFileInfo = sizeof(TMPQUserData); + nInfoType = SFILE_INFO_TYPE_READ_FROM_FILE; } + break; - // Is the file compressed by PKWARE Data Compression Library ? - else if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE) + case SFileMpqUserData: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL && ha->pUserData != NULL) { - nResult = SCompExplode(pbOutSector, &cbOutSector, pbInSector, cbInSector); + ByteOffset = ha->UserDataPos + sizeof(TMPQUserData); + cbSrcFileInfo = ha->pUserData->dwHeaderOffs - sizeof(TMPQUserData); + nInfoType = SFILE_INFO_TYPE_READ_FROM_FILE; } + break; - // Did the decompression fail ? - if(nResult == 0) + case SFileMpqHeaderOffset: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL) { - nError = ERROR_FILE_CORRUPT; - break; + pvSrcFileInfo = &ha->MpqPos; + cbSrcFileInfo = sizeof(ULONGLONG); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } - } - 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_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); - } + break; - // 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) + case SFileMpqHeaderSize: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL) { - if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2) - nResult = SCompDecompress2(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer); - else - nResult = SCompDecompress(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer); + pvSrcFileInfo = &ha->pHeader->dwHeaderSize; + cbSrcFileInfo = sizeof(DWORD); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } + break; - // 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); - - 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 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; - int nError = 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) - { - nError = AllocateSectorBuffer(hf); - if(nError != ERROR_SUCCESS) - return nError; - - // 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; - - if(!SCompDecompressMpk(hf->pbFileSector, &cbOutBuffer, pbRawData, (int)pFileEntry->dwCmpSize)) - nError = 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 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) - 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 = ReadMpqFileSectorFile(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; -} - -static int ReadMpqFileLocalFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead) -{ - ULONGLONG FilePosition1 = dwFilePos; - ULONGLONG FilePosition2; - DWORD dwBytesRead = 0; - int nError = 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((nError = GetLastError()) == ERROR_HANDLE_EOF) - { - FileStream_GetPos(hf->pStream, &FilePosition2); - dwBytesRead = (DWORD)(FilePosition2 - FilePosition1); - } - } - else - { - dwBytesRead = dwToRead; - } - - *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(hFile)) - { - 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) - { - nError = ReadMpqFileLocalFile(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); - } - - // If the file is a patch file, we have to read it special way - else if(hf->hfPatchFile != NULL && (hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0) - { - nError = 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) - { - nError = 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) - { - nError = ReadMpqFileSingleUnit(hf, pvBuffer, hf->dwFilePos, dwToRead, &dwBytesRead); - } - - // Otherwise read it as sector based MPQ file - else - { - nError = 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(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(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->hfPatchFile != NULL) - { - // Walk through the entire patch chain, take the last version - while(hf != NULL) + case SFileMpqHeader: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != 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; + ByteOffset = ha->MpqPos; + cbSrcFileInfo = ha->pHeader->dwHeaderSize; + nInfoType = SFILE_INFO_TYPE_READ_FROM_FILE; } - } - else - { - // Is it a local file ? - if(hf->pStream != NULL) + break; + + case SFileMpqHetTableOffset: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL) { - FileStream_GetSize(hf->pStream, &FileSize); + pvSrcFileInfo = &ha->pHeader->HetTablePos64; + cbSrcFileInfo = sizeof(ULONGLONG); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } - else + break; + + case SFileMpqHetTableSize: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL) { - FileSize = hf->dwDataSize; + pvSrcFileInfo = &ha->pHeader->HetTableSize64; + cbSrcFileInfo = sizeof(ULONGLONG); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } - } - - // 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(hFile)) - { - 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) + case SFileMpqHetHeader: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL) { - FileStream_GetPos(hf->pStream, &FilePosition); + pvSrcFileInfo = LoadExtTable(ha, ha->pHeader->HetTablePos64, (size_t)ha->pHeader->HetTableSize64, HET_TABLE_SIGNATURE, MPQ_KEY_HASH_TABLE); + cbSrcFileInfo = sizeof(TMPQHetHeader); + nInfoType = SFILE_INFO_TYPE_ALLOCATED; } - else + break; + + case SFileMpqHetTable: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL) { - FilePosition = hf->dwFilePos; + pvSrcFileInfo = LoadHetTable(ha); + cbSrcFileInfo = sizeof(void *); + nInfoType = SFILE_INFO_TYPE_TABLE_POINTER; } break; - case FILE_END: - if(hf->pStream != NULL) + case SFileMpqBetTableOffset: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL) { - FileStream_GetSize(hf->pStream, &FilePosition); + pvSrcFileInfo = &ha->pHeader->BetTablePos64; + cbSrcFileInfo = sizeof(ULONGLONG); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } - else + break; + + case SFileMpqBetTableSize: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL) { - FilePosition = SFileGetFileSize(hFile, NULL); + pvSrcFileInfo = &ha->pHeader->BetTableSize64; + cbSrcFileInfo = sizeof(ULONGLONG); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } 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 + case SFileMpqBetHeader: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL) + { + pvSrcFileInfo = LoadExtTable(ha, ha->pHeader->BetTablePos64, (size_t)ha->pHeader->BetTableSize64, BET_TABLE_SIGNATURE, MPQ_KEY_BLOCK_TABLE); + if(pvSrcFileInfo != NULL) + { + // It is allowed for the caller to only require BET header. + cbSrcFileInfo = sizeof(TMPQBetHeader) + ((TMPQBetHeader *)pvSrcFileInfo)->dwFlagCount * sizeof(DWORD); + if(cbFileInfo == sizeof(TMPQBetHeader)) + cbSrcFileInfo = sizeof(TMPQBetHeader); + nInfoType = SFILE_INFO_TYPE_ALLOCATED; + } + } + break; -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 -}; + case SFileMpqBetTable: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL) + { + pvSrcFileInfo = LoadBetTable(ha); + cbSrcFileInfo = sizeof(void *); + nInfoType = SFILE_INFO_TYPE_TABLE_POINTER; + } + break; -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 -}; + case SFileMpqHashTableOffset: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL) + { + Int64Value = MAKE_OFFSET64(ha->pHeader->wHashTablePosHi, ha->pHeader->dwHashTablePos); + pvSrcFileInfo = &Int64Value; + cbSrcFileInfo = sizeof(ULONGLONG); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; + } + break; -static int CreatePseudoFileName(HANDLE hFile, TFileEntry * pFileEntry, char * szFileName) -{ - TMPQFile * hf = (TMPQFile *)hFile; // MPQ File handle - DWORD FirstBytes[2] = {0, 0}; // The first 4 bytes of the file - DWORD dwBytesRead = 0; - DWORD dwFilePos; // Saved file position + case SFileMpqHashTableSize64: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL) + { + pvSrcFileInfo = &ha->pHeader->HashTableSize64; + cbSrcFileInfo = sizeof(ULONGLONG); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; + } + break; - // Read the first 2 DWORDs bytes from the file - dwFilePos = SFileSetFilePointer(hFile, 0, NULL, FILE_CURRENT); - SFileReadFile(hFile, FirstBytes, sizeof(FirstBytes), &dwBytesRead, NULL); - SFileSetFilePointer(hFile, dwFilePos, NULL, FILE_BEGIN); + case SFileMpqHashTableSize: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL) + { + pvSrcFileInfo = &ha->pHeader->dwHashTableSize; + cbSrcFileInfo = sizeof(DWORD); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; + } + break; - // If we read at least 8 bytes - if(dwBytesRead == sizeof(FirstBytes)) - { - // Make sure that the array is properly BSWAP-ed - BSWAP_ARRAY32_UNSIGNED(FirstBytes, sizeof(FirstBytes)); + case SFileMpqHashTable: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL) + { + cbSrcFileInfo = ha->pHeader->dwHashTableSize * sizeof(TMPQHash); + pvSrcFileInfo = ha->pHashTable; + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; + } + break; - // Try to guess file extension from those 2 DWORDs - for(size_t i = 0; data2ext[i].szExt != NULL; i++) - { - if((FirstBytes[0] & data2ext[i].dwOffset00Mask) == data2ext[i].dwOffset00Data && - (FirstBytes[1] & data2ext[i].dwOffset04Mask) == data2ext[i].dwOffset04Data) + case SFileMpqBlockTableOffset: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL) { - char szPseudoName[20] = ""; + Int64Value = MAKE_OFFSET64(ha->pHeader->wBlockTablePosHi, ha->pHeader->dwBlockTablePos); + pvSrcFileInfo = &Int64Value; + cbSrcFileInfo = sizeof(ULONGLONG); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; + } + break; - // Format the pseudo-name - sprintf(szPseudoName, "File%08u.%s", (unsigned int)(pFileEntry - hf->ha->pFileTable), data2ext[i].szExt); + case SFileMpqBlockTableSize64: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL) + { + pvSrcFileInfo = &ha->pHeader->BlockTableSize64; + cbSrcFileInfo = sizeof(ULONGLONG); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; + } + break; - // Save the pseudo-name in the file entry as well - AllocateFileName(hf->ha, pFileEntry, szPseudoName); + case SFileMpqBlockTableSize: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL) + { + pvSrcFileInfo = &ha->pHeader->dwBlockTableSize; + cbSrcFileInfo = sizeof(DWORD); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; + } + break; - // If the caller wants to copy the file name, do it - if(szFileName != NULL) - strcpy(szFileName, szPseudoName); - return ERROR_SUCCESS; + case SFileMpqBlockTable: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL) + { + cbSrcFileInfo = ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock); + if(cbFileInfo >= cbSrcFileInfo) + pvSrcFileInfo = LoadBlockTable(ha, true); + nInfoType = SFILE_INFO_TYPE_ALLOCATED; } - } - } + break; - return ERROR_NOT_SUPPORTED; -} + case SFileMpqHiBlockTableOffset: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL) + { + pvSrcFileInfo = &ha->pHeader->HiBlockTablePos64; + cbSrcFileInfo = sizeof(ULONGLONG); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; + } + break; -bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName) -{ - TMPQFile * hf = (TMPQFile *)hFile; // MPQ File handle - TCHAR * szFileNameT; - int nError = ERROR_INVALID_HANDLE; + case SFileMpqHiBlockTableSize64: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL) + { + pvSrcFileInfo = &ha->pHeader->HiBlockTableSize64; + cbSrcFileInfo = sizeof(ULONGLONG); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; + } + break; - // Pre-zero the output buffer - if(szFileName != NULL) - *szFileName = 0; + case SFileMpqHiBlockTable: + assert(false); + break; - // Check valid parameters - if(IsValidFileHandle(hFile)) - { - TFileEntry * pFileEntry = hf->pFileEntry; + case SFileMpqSignatures: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL && QueryMpqSignatureInfo(ha, &SignatureInfo)) + { + pvSrcFileInfo = &SignatureInfo.SignatureTypes; + cbSrcFileInfo = sizeof(DWORD); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; + } + break; - // For MPQ files, retrieve the file name from the file entry - if(hf->pStream == NULL) - { - if(pFileEntry != NULL) + case SFileMpqStrongSignatureOffset: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL && QueryMpqSignatureInfo(ha, &SignatureInfo)) { - // If the file name is not there yet, create a pseudo name - if(pFileEntry->szFileName == NULL) + // Is a strong signature present? + if(SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG) { - nError = CreatePseudoFileName(hFile, pFileEntry, szFileName); + pvSrcFileInfo = &SignatureInfo.EndMpqData; + cbSrcFileInfo = sizeof(ULONGLONG); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } - else + } + break; + + case SFileMpqStrongSignatureSize: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL && QueryMpqSignatureInfo(ha, &SignatureInfo)) + { + // Is a strong signature present? + if(SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG) { - if(szFileName != NULL) - strcpy(szFileName, pFileEntry->szFileName); - nError = ERROR_SUCCESS; + dwInt32Value = MPQ_STRONG_SIGNATURE_SIZE + 4; + pvSrcFileInfo = &dwInt32Value; + cbSrcFileInfo = sizeof(DWORD); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } } - } + break; - // For local files, copy the file name from the stream - else - { - if(szFileName != NULL) + case SFileMpqStrongSignature: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL && QueryMpqSignatureInfo(ha, &SignatureInfo)) { - szFileNameT = FileStream_GetFileName(hf->pStream); - CopyFileName(szFileName, szFileNameT, _tcslen(szFileNameT)); + // Is a strong signature present? + if(SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG) + { + pvSrcFileInfo = SignatureInfo.Signature; + cbSrcFileInfo = MPQ_STRONG_SIGNATURE_SIZE + 4; + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; + } } - nError = ERROR_SUCCESS; - } - } - - if(nError != ERROR_SUCCESS) - SetLastError(nError); - 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 - -bool WINAPI SFileGetFileInfo( - HANDLE hMpqOrFile, - DWORD dwInfoType, - void * pvFileInfo, - DWORD cbFileInfo, - LPDWORD pcbLengthNeeded) -{ - TMPQArchive * ha = NULL; - TMPQBlock * pBlockTable = NULL; - ULONGLONG Int64Value = 0; - TMPQFile * hf = NULL; - TCHAR * szPatchChain = NULL; - void * pvSrcFileInfo = NULL; - DWORD cbSrcFileInfo = 0; - DWORD dwInt32Value = 0; - int nError = ERROR_INVALID_PARAMETER; + break; - switch(dwInfoType) - { - case SFILE_INFO_ARCHIVE_NAME: + case SFileMpqBitmapOffset: ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) + if(ha != NULL && ha->pBitmap != NULL) { - pvSrcFileInfo = FileStream_GetFileName(ha->pStream); - cbSrcFileInfo = (DWORD)(_tcslen((TCHAR *)pvSrcFileInfo) + 1) * sizeof(TCHAR); + Int64Value = MAKE_OFFSET64(ha->pBitmap->dwMapOffsetHi, ha->pBitmap->dwMapOffsetLo); + pvSrcFileInfo = &Int64Value; + cbSrcFileInfo = sizeof(ULONGLONG); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; - case SFILE_INFO_ARCHIVE_SIZE: // Size of the archive + case SFileMpqBitmapSize: ha = IsValidMpqHandle(hMpqOrFile); - if(ha != NULL) + if(ha != NULL && ha->pBitmap != NULL) { - pvSrcFileInfo = &ha->pHeader->dwArchiveSize; + pvSrcFileInfo = &ha->dwBitmapSize; cbSrcFileInfo = sizeof(DWORD); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; - case SFILE_INFO_MAX_FILE_COUNT: // Max. number of files in the MPQ + case SFileMpqBitmap: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { - pvSrcFileInfo = &ha->dwMaxFileCount; - cbSrcFileInfo = sizeof(DWORD); + pvSrcFileInfo = ha->pBitmap; + cbSrcFileInfo = ha->dwBitmapSize; + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; - case SFILE_INFO_HASH_TABLE_SIZE: // Size of the hash table + case SFileMpqArchiveSize64: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { - pvSrcFileInfo = &ha->pHeader->dwHashTableSize; + pvSrcFileInfo = &ha->pHeader->ArchiveSize64; cbSrcFileInfo = sizeof(DWORD); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; - case SFILE_INFO_BLOCK_TABLE_SIZE: // Size of the block table + case SFileMpqArchiveSize: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { - pvSrcFileInfo = &ha->pHeader->dwBlockTableSize; + pvSrcFileInfo = &ha->pHeader->dwArchiveSize; cbSrcFileInfo = sizeof(DWORD); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; - case SFILE_INFO_SECTOR_SIZE: + case SFileMpqMaxFileCount: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { - pvSrcFileInfo = &ha->dwSectorSize; + pvSrcFileInfo = &ha->dwMaxFileCount; cbSrcFileInfo = sizeof(DWORD); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; - case SFILE_INFO_HASH_TABLE: + case SFileMpqFileTableSize: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { - pvSrcFileInfo = ha->pHashTable; - cbSrcFileInfo = ha->pHeader->dwHashTableSize * sizeof(TMPQHash); + pvSrcFileInfo = &ha->dwFileTableSize; + cbSrcFileInfo = sizeof(DWORD); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; - case SFILE_INFO_BLOCK_TABLE: + case SFileMpqSectorSize: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { - pvSrcFileInfo = pBlockTable = TranslateBlockTable(ha, &Int64Value, NULL); - cbSrcFileInfo = (DWORD)(Int64Value / sizeof(TMPQBlock)); + pvSrcFileInfo = &ha->dwSectorSize; + cbSrcFileInfo = sizeof(DWORD); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; - case SFILE_INFO_NUM_FILES: + case SFileMpqNumberOfFiles: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &dwInt32Value; cbSrcFileInfo = sizeof(DWORD); dwInt32Value = GetMpqFileCount(ha); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; + } + break; + + case SFileMpqRawChunkSize: + ha = IsValidMpqHandle(hMpqOrFile); + if(ha != NULL) + { + pvSrcFileInfo = &ha->pHeader->dwRawChunkSize; + cbSrcFileInfo = sizeof(DWORD); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; - case SFILE_INFO_STREAM_FLAGS: + case SFileMpqStreamFlags: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { FileStream_GetFlags(ha->pStream, &dwInt32Value); pvSrcFileInfo = &dwInt32Value; cbSrcFileInfo = sizeof(DWORD); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; - case SFILE_INFO_IS_READ_ONLY: + case SFileMpqIsReadOnly: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { + dwInt32Value = (FileStream_IsReadOnly(ha->pStream) || (ha->dwFlags & MPQ_FLAG_READ_ONLY)); pvSrcFileInfo = &dwInt32Value; cbSrcFileInfo = sizeof(DWORD); - dwInt32Value = (FileStream_IsReadOnly(ha->pStream) || (ha->dwFlags & MPQ_FLAG_READ_ONLY)); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; - case SFILE_INFO_HASH_INDEX: + case SFileInfoPatchChain: + hf = IsValidFileHandle(hMpqOrFile); + if(hf != NULL) + return GetFilePatchChain(hf, pvFileInfo, cbFileInfo, pcbLengthNeeded); + break; + + case SFileInfoFileEntry: + hf = IsValidFileHandle(hMpqOrFile); + if(hf != NULL && hf->pFileEntry != NULL) + { + pvSrcFileInfo = pFileEntry = hf->pFileEntry; + cbSrcFileInfo = sizeof(TFileEntry); + if(pFileEntry->szFileName != NULL) + cbSrcFileInfo += (DWORD)strlen(pFileEntry->szFileName) + 1; + nInfoType = SFILE_INFO_TYPE_FILE_ENTRY; + } + break; + + case SFileInfoHashEntry: + hf = IsValidFileHandle(hMpqOrFile); + if(hf != NULL && hf->ha != NULL && hf->ha->pHashTable != NULL) + { + pvSrcFileInfo = hf->ha->pHashTable + hf->pFileEntry->dwHashIndex; + cbSrcFileInfo = sizeof(TMPQHash); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; + } + break; + + case SFileInfoHashIndex: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL) { pvSrcFileInfo = &hf->pFileEntry->dwHashIndex; cbSrcFileInfo = sizeof(DWORD); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; - case SFILE_INFO_CODENAME1: + case SFileInfoNameHash1: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->ha != NULL && hf->ha->pHashTable != NULL) { pvSrcFileInfo = &ha->pHashTable[hf->pFileEntry->dwHashIndex].dwName1; cbSrcFileInfo = sizeof(DWORD); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; - case SFILE_INFO_CODENAME2: + case SFileInfoNameHash2: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->ha != NULL && hf->ha->pHashTable != NULL) { pvSrcFileInfo = &ha->pHashTable[hf->pFileEntry->dwHashIndex].dwName2; cbSrcFileInfo = sizeof(DWORD); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; + } + break; + + case SFileInfoNameHash3: + hf = IsValidFileHandle(hMpqOrFile); + if(hf != NULL && hf->pFileEntry != NULL) + { + pvSrcFileInfo = &hf->pFileEntry->FileNameHash; + cbSrcFileInfo = sizeof(ULONGLONG); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; - case SFILE_INFO_LOCALEID: + case SFileInfoLocale: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL) { + dwInt32Value = hf->pFileEntry->lcLocale; pvSrcFileInfo = &dwInt32Value; cbSrcFileInfo = sizeof(DWORD); - dwInt32Value = hf->pFileEntry->lcLocale; + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; - case SFILE_INFO_BLOCKINDEX: + case SFileInfoFileIndex: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->ha != NULL) { + dwInt32Value = (DWORD)(hf->pFileEntry - hf->ha->pFileTable); pvSrcFileInfo = &dwInt32Value; cbSrcFileInfo = sizeof(DWORD); - dwInt32Value = (DWORD)(hf->pFileEntry - hf->ha->pFileTable); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; + } + break; + + case SFileInfoByteOffset: + hf = IsValidFileHandle(hMpqOrFile); + if(hf != NULL && hf->pFileEntry != NULL) + { + pvSrcFileInfo = &hf->pFileEntry->ByteOffset; + cbSrcFileInfo = sizeof(ULONGLONG); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; - case SFILE_INFO_FILE_SIZE: + case SFileInfoFileTime: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL) { - pvSrcFileInfo = &hf->pFileEntry->dwFileSize; - cbSrcFileInfo = sizeof(DWORD); + pvSrcFileInfo = &hf->pFileEntry->FileTime; + cbSrcFileInfo = sizeof(ULONGLONG); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; - case SFILE_INFO_COMPRESSED_SIZE: + case SFileInfoFileSize: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL) { - pvSrcFileInfo = &hf->pFileEntry->dwCmpSize; + pvSrcFileInfo = &hf->pFileEntry->dwFileSize; cbSrcFileInfo = sizeof(DWORD); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; - case SFILE_INFO_FLAGS: + case SFileInfoCompressedSize: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL) { - pvSrcFileInfo = &hf->pFileEntry->dwFlags; + pvSrcFileInfo = &hf->pFileEntry->dwCmpSize; cbSrcFileInfo = sizeof(DWORD); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; - case SFILE_INFO_POSITION: + case SFileInfoFlags: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL) { - pvSrcFileInfo = &hf->pFileEntry->ByteOffset; - cbSrcFileInfo = sizeof(ULONGLONG); + pvSrcFileInfo = &hf->pFileEntry->dwFlags; + cbSrcFileInfo = sizeof(DWORD); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; - case SFILE_INFO_KEY: + case SFileInfoEncryptionKey: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL) { pvSrcFileInfo = &hf->dwFileKey; cbSrcFileInfo = sizeof(DWORD); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; - case SFILE_INFO_KEY_UNFIXED: + case SFileInfoEncryptionKeyRaw: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL) { @@ -1290,52 +750,222 @@ bool WINAPI SFileGetFileInfo( dwInt32Value = (dwInt32Value ^ hf->pFileEntry->dwFileSize) - (DWORD)hf->MpqFilePos; pvSrcFileInfo = &dwInt32Value; cbSrcFileInfo = sizeof(DWORD); + nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; - case SFILE_INFO_FILETIME: - hf = IsValidFileHandle(hMpqOrFile); - if(hf != NULL) - { - pvSrcFileInfo = &hf->pFileEntry->FileTime; - cbSrcFileInfo = sizeof(ULONGLONG); - } - break; - - case SFILE_INFO_PATCH_CHAIN: - hf = IsValidFileHandle(hMpqOrFile); - if(hf != NULL) - pvSrcFileInfo = szPatchChain = GetFilePatchChain(hf, &cbSrcFileInfo); - break; + default: // Invalid info class + SetLastError(ERROR_INVALID_PARAMETER); + return false; } - // Check if one of the cases case yielded a result - if(pvSrcFileInfo != NULL && pvFileInfo != NULL) + // If we validated the handle and info class, give as much info as possible + if(nInfoType != SFILE_INFO_TYPE_UNKNOWN) { - // Give the length needed + // Give the length needed, if wanted if(pcbLengthNeeded != NULL) pcbLengthNeeded[0] = cbSrcFileInfo; - // Verify if we have enough space in the output buffer - if(cbSrcFileInfo <= cbFileInfo) + // If the caller entered an output buffer, the output size must also be entered + if(pvFileInfo != NULL && cbFileInfo != 0) { - memcpy(pvFileInfo, pvSrcFileInfo, cbSrcFileInfo); - nError = ERROR_SUCCESS; + // Check if there is enough space in the output buffer + if(cbSrcFileInfo <= cbFileInfo) + { + switch(nInfoType) + { + case SFILE_INFO_TYPE_DIRECT_POINTER: + case SFILE_INFO_TYPE_ALLOCATED: + memcpy(pvFileInfo, pvSrcFileInfo, cbSrcFileInfo); + nError = ERROR_SUCCESS; + break; + + case SFILE_INFO_TYPE_READ_FROM_FILE: + if(FileStream_Read(ha->pStream, &ByteOffset, pvFileInfo, cbSrcFileInfo)) + nError = ERROR_SUCCESS; + break; + + case SFILE_INFO_TYPE_TABLE_POINTER: + *(void **)pvFileInfo = pvSrcFileInfo; + pvSrcFileInfo = NULL; + nError = ERROR_SUCCESS; + break; + + case SFILE_INFO_TYPE_FILE_ENTRY: + assert(pFileEntry != NULL); + ConvertFileEntryToSelfRelative((TFileEntry *)pvFileInfo, pFileEntry); + nError = ERROR_SUCCESS; + break; + } + } + else + { + nError = ERROR_INSUFFICIENT_BUFFER; + } } else { - nError = ERROR_INSUFFICIENT_BUFFER; + nError = ERROR_SUCCESS; } - } - // Free the allocated buffers, if any - if(szPatchChain != NULL) - STORM_FREE(szPatchChain); - if(pBlockTable != NULL) - STORM_FREE(pBlockTable); + // Free the file info if needed + if(nInfoType == SFILE_INFO_TYPE_ALLOCATED && pvSrcFileInfo != NULL) + STORM_FREE(pvSrcFileInfo); + if(nInfoType == SFILE_INFO_TYPE_TABLE_POINTER && pvSrcFileInfo != NULL) + SFileFreeFileInfo(pvSrcFileInfo, InfoClass); + } // Set the last error value, if needed if(nError != ERROR_SUCCESS) SetLastError(nError); return (nError == ERROR_SUCCESS); } + +bool WINAPI SFileFreeFileInfo(void * pvFileInfo, SFileInfoClass InfoClass) +{ + switch(InfoClass) + { + case SFileMpqHetTable: + FreeHetTable((TMPQHetTable *)pvFileInfo); + return true; + + case SFileMpqBetTable: + FreeBetTable((TMPQBetTable *)pvFileInfo); + return true; + } + + SetLastError(ERROR_INVALID_PARAMETER); + return false; +} + +//----------------------------------------------------------------------------- +// Tries to retrieve the file name + +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 +}; + +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 +}; + +static int CreatePseudoFileName(HANDLE hFile, TFileEntry * pFileEntry, char * szFileName) +{ + TMPQFile * hf = (TMPQFile *)hFile; // MPQ File handle + DWORD FirstBytes[2] = {0, 0}; // The first 4 bytes of the file + DWORD dwBytesRead = 0; + DWORD dwFilePos; // Saved file position + + // Read the first 2 DWORDs bytes from the file + dwFilePos = SFileSetFilePointer(hFile, 0, NULL, FILE_CURRENT); + SFileReadFile(hFile, FirstBytes, sizeof(FirstBytes), &dwBytesRead, NULL); + SFileSetFilePointer(hFile, dwFilePos, NULL, FILE_BEGIN); + + // If we read at least 8 bytes + if(dwBytesRead == sizeof(FirstBytes)) + { + // Make sure that the array is properly BSWAP-ed + BSWAP_ARRAY32_UNSIGNED(FirstBytes, sizeof(FirstBytes)); + + // Try to guess file extension from those 2 DWORDs + for(size_t i = 0; data2ext[i].szExt != NULL; i++) + { + if((FirstBytes[0] & data2ext[i].dwOffset00Mask) == data2ext[i].dwOffset00Data && + (FirstBytes[1] & data2ext[i].dwOffset04Mask) == data2ext[i].dwOffset04Data) + { + char szPseudoName[20] = ""; + + // Format the pseudo-name + sprintf(szPseudoName, "File%08u.%s", (unsigned int)(pFileEntry - hf->ha->pFileTable), data2ext[i].szExt); + + // Save the pseudo-name in the file entry as well + AllocateFileName(hf->ha, pFileEntry, szPseudoName); + + // If the caller wants to copy the file name, do it + if(szFileName != NULL) + strcpy(szFileName, szPseudoName); + return ERROR_SUCCESS; + } + } + } + + return ERROR_NOT_SUPPORTED; +} + +bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName) +{ + TMPQFile * hf = (TMPQFile *)hFile; // MPQ File handle + int nError = ERROR_INVALID_HANDLE; + + // Pre-zero the output buffer + if(szFileName != NULL) + *szFileName = 0; + + // Check valid parameters + if(IsValidFileHandle(hFile)) + { + TFileEntry * pFileEntry = hf->pFileEntry; + + // For MPQ files, retrieve the file name from the file entry + if(hf->pStream == NULL) + { + if(pFileEntry != NULL) + { + // If the file name is not there yet, create a pseudo name + if(pFileEntry->szFileName == NULL) + { + nError = CreatePseudoFileName(hFile, pFileEntry, szFileName); + } + else + { + if(szFileName != NULL) + strcpy(szFileName, pFileEntry->szFileName); + nError = ERROR_SUCCESS; + } + } + } + + // For local files, copy the file name from the stream + else + { + if(szFileName != NULL) + { + const TCHAR * szStreamName = FileStream_GetFileName(hf->pStream); + CopyFileName(szFileName, szStreamName, _tcslen(szStreamName)); + } + nError = ERROR_SUCCESS; + } + } + + if(nError != ERROR_SUCCESS) + SetLastError(nError); + return (nError == ERROR_SUCCESS); +} + |