/*****************************************************************************/ /* SFileGetFileInfo.cpp Copyright (c) Ladislav Zezula 2013 */ /*---------------------------------------------------------------------------*/ /* Description: */ /*---------------------------------------------------------------------------*/ /* Date Ver Who Comment */ /* -------- ---- --- ------- */ /* 30.11.13 1.00 Lad The first version of SFileGetFileInfo.cpp */ /*****************************************************************************/ #define __STORMLIB_SELF__ #include "StormLib.h" #include "StormCommon.h" //----------------------------------------------------------------------------- // Local functions 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 GetInfo_ReturdwErrCode(DWORD dwErrCode) { SetLastError(dwErrCode); return false; } static bool GetInfo_BufferCheck(void * pvFileInfo, DWORD cbFileInfo, DWORD cbData, LPDWORD pcbLengthNeeded) { // Give the length needed to store the info if(pcbLengthNeeded != NULL) pcbLengthNeeded[0] = cbData; // Check for sufficient buffer if(cbData > cbFileInfo) return GetInfo_ReturdwErrCode(ERROR_INSUFFICIENT_BUFFER); // If the buffer size is sufficient, check for valid user buffer if(pvFileInfo == NULL) return GetInfo_ReturdwErrCode(ERROR_INVALID_PARAMETER); // Buffers and sizes are OK, we are ready to proceed file copying return true; } static bool GetInfo(void * pvFileInfo, DWORD cbFileInfo, const void * pvData, DWORD cbData, LPDWORD pcbLengthNeeded) { // Verify buffer pointer and buffer size if(!GetInfo_BufferCheck(pvFileInfo, cbFileInfo, cbData, pcbLengthNeeded)) return false; // Copy the data to the caller-supplied buffer memcpy(pvFileInfo, pvData, cbData); return true; } static bool GetInfo_Allocated(void * pvFileInfo, DWORD cbFileInfo, void * pvData, DWORD cbData, LPDWORD pcbLengthNeeded) { bool bResult; // Verify buffer pointer and buffer size if((bResult = GetInfo_BufferCheck(pvFileInfo, cbFileInfo, cbData, pcbLengthNeeded)) != false) memcpy(pvFileInfo, pvData, cbData); // Copy the data to the user buffer STORM_FREE(pvData); return bResult; } static bool GetInfo_TablePointer(void * pvFileInfo, DWORD cbFileInfo, void * pvTablePointer, SFileInfoClass InfoClass, LPDWORD pcbLengthNeeded) { // Verify buffer pointer and buffer size if(!GetInfo_BufferCheck(pvFileInfo, cbFileInfo, sizeof(void *), pcbLengthNeeded)) { SFileFreeFileInfo(pvTablePointer, InfoClass); return false; } // The user buffer receives pointer to the table. // When done, the caller needs to call SFileFreeFileInfo on it *(void **)pvFileInfo = pvTablePointer; return true; } static bool GetInfo_ReadFromFile(void * pvFileInfo, DWORD cbFileInfo, TFileStream * pStream, ULONGLONG ByteOffset, DWORD cbData, LPDWORD pcbLengthNeeded) { // Verify buffer pointer and buffer size if(!GetInfo_BufferCheck(pvFileInfo, cbFileInfo, cbData, pcbLengthNeeded)) return false; return FileStream_Read(pStream, &ByteOffset, pvFileInfo, cbData); } static bool GetInfo_FileEntry(void * pvFileInfo, DWORD cbFileInfo, TFileEntry * pFileEntry, LPDWORD pcbLengthNeeded) { LPBYTE pbFileInfo = (LPBYTE)pvFileInfo; DWORD cbSrcFileInfo = sizeof(TFileEntry); DWORD cbFileName = 1; // The file name belongs to the file entry if(pFileEntry->szFileName) cbFileName = (DWORD)strlen(pFileEntry->szFileName) + 1; cbSrcFileInfo += cbFileName; // Verify buffer pointer and buffer size if(!GetInfo_BufferCheck(pvFileInfo, cbFileInfo, cbSrcFileInfo, pcbLengthNeeded)) return false; // Copy the file entry memcpy(pbFileInfo, pFileEntry, sizeof(TFileEntry)); pbFileInfo += sizeof(TFileEntry); pbFileInfo[0] = 0; // Copy the file name if(pFileEntry->szFileName) memcpy(pbFileInfo, pFileEntry->szFileName, cbFileName); return true; } static bool GetInfo_PatchChain(TMPQFile * hf, void * pvFileInfo, DWORD cbFileInfo, LPDWORD pcbLengthNeeded) { TMPQFile * hfTemp; LPCTSTR szPatchName; LPTSTR szFileInfo = (LPTSTR)pvFileInfo; size_t cchCharsNeeded = 1; size_t nLength; // Patch chain is only supported on MPQ files. Local files are not supported. if(hf->pStream != NULL) return GetInfo_ReturdwErrCode(ERROR_INVALID_PARAMETER); // Calculate the necessary length of the multi-string for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatch) cchCharsNeeded += _tcslen(FileStream_GetFileName(hfTemp->ha->pStream)) + 1; // Verify whether the caller gave us valid buffer with enough size if(!GetInfo_BufferCheck(pvFileInfo, cbFileInfo, (DWORD)(cchCharsNeeded * sizeof(TCHAR)), pcbLengthNeeded)) return false; // Copy each patch name for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatch) { // Get the file name and its length szPatchName = FileStream_GetFileName(hfTemp->ha->pStream); nLength = _tcslen(szPatchName) + 1; // Copy the file name memcpy(szFileInfo, szPatchName, nLength * sizeof(TCHAR)); szFileInfo += nLength; } // Make it multi-string szFileInfo[0] = 0; return true; } //----------------------------------------------------------------------------- // 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 bool WINAPI SFileGetFileInfo( HANDLE hMpqOrFile, SFileInfoClass InfoClass, void * pvFileInfo, DWORD cbFileInfo, LPDWORD pcbLengthNeeded) { MPQ_SIGNATURE_INFO SignatureInfo; const TCHAR * szSrcFileInfo; TMPQArchive * ha = NULL; TFileEntry * pFileEntry = NULL; TMPQHeader * pHeader = NULL; ULONGLONG Int64Value = 0; TMPQFile * hf = NULL; void * pvSrcFileInfo = NULL; DWORD cbSrcFileInfo = 0; DWORD dwInt32Value = 0; // Validate archive/file handle if((int)InfoClass <= (int)SFileMpqFlags) { if((ha = IsValidMpqHandle(hMpqOrFile)) == NULL) return GetInfo_ReturdwErrCode(ERROR_INVALID_HANDLE); pHeader = ha->pHeader; } else { if((hf = IsValidFileHandle(hMpqOrFile)) == NULL) return GetInfo_ReturdwErrCode(ERROR_INVALID_HANDLE); pFileEntry = hf->pFileEntry; } // Return info-class-specific data switch(InfoClass) { case SFileMpqFileName: szSrcFileInfo = FileStream_GetFileName(ha->pStream); cbSrcFileInfo = (DWORD)((_tcslen(szSrcFileInfo) + 1) * sizeof(TCHAR)); return GetInfo(pvFileInfo, cbFileInfo, szSrcFileInfo, cbSrcFileInfo, pcbLengthNeeded); case SFileMpqStreamBitmap: return FileStream_GetBitmap(ha->pStream, pvFileInfo, cbFileInfo, pcbLengthNeeded); case SFileMpqUserDataOffset: return GetInfo(pvFileInfo, cbFileInfo, &ha->UserDataPos, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqUserDataHeader: if(ha->pUserData == NULL) return GetInfo_ReturdwErrCode(ERROR_INVALID_PARAMETER); return GetInfo_ReadFromFile(pvFileInfo, cbFileInfo, ha->pStream, ha->UserDataPos, sizeof(TMPQUserData), pcbLengthNeeded); case SFileMpqUserData: if(ha->pUserData == NULL) return GetInfo_ReturdwErrCode(ERROR_INVALID_PARAMETER); return GetInfo_ReadFromFile(pvFileInfo, cbFileInfo, ha->pStream, ha->UserDataPos + sizeof(TMPQUserData), ha->pUserData->dwHeaderOffs - sizeof(TMPQUserData), pcbLengthNeeded); case SFileMpqHeaderOffset: return GetInfo(pvFileInfo, cbFileInfo, &ha->MpqPos, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqHeaderSize: return GetInfo(pvFileInfo, cbFileInfo, &pHeader->dwHeaderSize, sizeof(DWORD), pcbLengthNeeded); case SFileMpqHeader: return GetInfo_ReadFromFile(pvFileInfo, cbFileInfo, ha->pStream, ha->MpqPos, pHeader->dwHeaderSize, pcbLengthNeeded); case SFileMpqHetTableOffset: return GetInfo(pvFileInfo, cbFileInfo, &pHeader->HetTablePos64, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqHetTableSize: return GetInfo(pvFileInfo, cbFileInfo, &pHeader->HetTableSize64, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqHetHeader: pvSrcFileInfo = LoadExtTable(ha, pHeader->HetTablePos64, (size_t)pHeader->HetTableSize64, HET_TABLE_SIGNATURE, MPQ_KEY_HASH_TABLE); if(pvSrcFileInfo == NULL) return GetInfo_ReturdwErrCode(ERROR_FILE_NOT_FOUND); return GetInfo_Allocated(pvFileInfo, cbFileInfo, pvSrcFileInfo, sizeof(TMPQHetHeader), pcbLengthNeeded); case SFileMpqHetTable: if((pvSrcFileInfo = LoadHetTable(ha)) == NULL) return GetInfo_ReturdwErrCode(ERROR_NOT_ENOUGH_MEMORY); return GetInfo_TablePointer(pvFileInfo, cbFileInfo, pvSrcFileInfo, InfoClass, pcbLengthNeeded); case SFileMpqBetTableOffset: return GetInfo(pvFileInfo, cbFileInfo, &pHeader->BetTablePos64, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqBetTableSize: return GetInfo(pvFileInfo, cbFileInfo, &pHeader->BetTableSize64, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqBetHeader: // Retrieve the table and its size pvSrcFileInfo = LoadExtTable(ha, pHeader->BetTablePos64, (size_t)pHeader->BetTableSize64, BET_TABLE_SIGNATURE, MPQ_KEY_BLOCK_TABLE); if(pvSrcFileInfo == NULL) return GetInfo_ReturdwErrCode(ERROR_FILE_NOT_FOUND); cbSrcFileInfo = sizeof(TMPQBetHeader) + ((TMPQBetHeader *)pvSrcFileInfo)->dwFlagCount * sizeof(DWORD); // It is allowed for the caller to only require BET header if(cbFileInfo == sizeof(TMPQBetHeader)) cbSrcFileInfo = sizeof(TMPQBetHeader); return GetInfo_Allocated(pvFileInfo, cbFileInfo, pvSrcFileInfo, cbSrcFileInfo, pcbLengthNeeded); case SFileMpqBetTable: if((pvSrcFileInfo = LoadBetTable(ha)) == NULL) return GetInfo_ReturdwErrCode(ERROR_NOT_ENOUGH_MEMORY); return GetInfo_TablePointer(pvFileInfo, cbFileInfo, pvSrcFileInfo, InfoClass, pcbLengthNeeded); case SFileMpqHashTableOffset: Int64Value = MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos); return GetInfo(pvFileInfo, cbFileInfo, &Int64Value, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqHashTableSize64: return GetInfo(pvFileInfo, cbFileInfo, &pHeader->HashTableSize64, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqHashTableSize: return GetInfo(pvFileInfo, cbFileInfo, &pHeader->dwHashTableSize, sizeof(DWORD), pcbLengthNeeded); case SFileMpqHashTable: cbSrcFileInfo = pHeader->dwHashTableSize * sizeof(TMPQHash); return GetInfo(pvFileInfo, cbFileInfo, ha->pHashTable, cbSrcFileInfo, pcbLengthNeeded); case SFileMpqBlockTableOffset: Int64Value = MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); return GetInfo(pvFileInfo, cbFileInfo, &Int64Value, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqBlockTableSize64: return GetInfo(pvFileInfo, cbFileInfo, &pHeader->BlockTableSize64, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqBlockTableSize: return GetInfo(pvFileInfo, cbFileInfo, &pHeader->dwBlockTableSize, sizeof(DWORD), pcbLengthNeeded); case SFileMpqBlockTable: if(MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos) >= ha->FileSize) return GetInfo_ReturdwErrCode(ERROR_FILE_NOT_FOUND); cbSrcFileInfo = pHeader->dwBlockTableSize * sizeof(TMPQBlock); pvSrcFileInfo = LoadBlockTable(ha, true); return GetInfo_Allocated(pvFileInfo, cbFileInfo, pvSrcFileInfo, cbSrcFileInfo, pcbLengthNeeded); case SFileMpqHiBlockTableOffset: return GetInfo(pvFileInfo, cbFileInfo, &pHeader->HiBlockTablePos64, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqHiBlockTableSize64: return GetInfo(pvFileInfo, cbFileInfo, &pHeader->HiBlockTableSize64, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqHiBlockTable: return GetInfo_ReturdwErrCode(ERROR_FILE_NOT_FOUND); case SFileMpqSignatures: if(!QueryMpqSignatureInfo(ha, &SignatureInfo)) return GetInfo_ReturdwErrCode(ERROR_FILE_NOT_FOUND); return GetInfo(pvFileInfo, cbFileInfo, &SignatureInfo.SignatureTypes, sizeof(DWORD), pcbLengthNeeded); case SFileMpqStrongSignatureOffset: if(QueryMpqSignatureInfo(ha, &SignatureInfo) == false || (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG) == 0) return GetInfo_ReturdwErrCode(ERROR_FILE_NOT_FOUND); return GetInfo(pvFileInfo, cbFileInfo, &SignatureInfo.EndMpqData, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqStrongSignatureSize: if(QueryMpqSignatureInfo(ha, &SignatureInfo) == false || (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG) == 0) return GetInfo_ReturdwErrCode(ERROR_FILE_NOT_FOUND); dwInt32Value = MPQ_STRONG_SIGNATURE_SIZE + 4; return GetInfo(pvFileInfo, cbFileInfo, &dwInt32Value, sizeof(DWORD), pcbLengthNeeded); case SFileMpqStrongSignature: if(QueryMpqSignatureInfo(ha, &SignatureInfo) == false || (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG) == 0) return GetInfo_ReturdwErrCode(ERROR_FILE_NOT_FOUND); return GetInfo(pvFileInfo, cbFileInfo, SignatureInfo.Signature, MPQ_STRONG_SIGNATURE_SIZE + 4, pcbLengthNeeded); case SFileMpqArchiveSize64: return GetInfo(pvFileInfo, cbFileInfo, &pHeader->ArchiveSize64, sizeof(ULONGLONG), pcbLengthNeeded); case SFileMpqArchiveSize: return GetInfo(pvFileInfo, cbFileInfo, &pHeader->dwArchiveSize, sizeof(DWORD), pcbLengthNeeded); case SFileMpqMaxFileCount: return GetInfo(pvFileInfo, cbFileInfo, &ha->dwMaxFileCount, sizeof(DWORD), pcbLengthNeeded); case SFileMpqFileTableSize: return GetInfo(pvFileInfo, cbFileInfo, &ha->dwFileTableSize, sizeof(DWORD), pcbLengthNeeded); case SFileMpqSectorSize: return GetInfo(pvFileInfo, cbFileInfo, &ha->dwSectorSize, sizeof(DWORD), pcbLengthNeeded); case SFileMpqNumberOfFiles: dwInt32Value = GetMpqFileCount(ha); return GetInfo(pvFileInfo, cbFileInfo, &dwInt32Value, sizeof(DWORD), pcbLengthNeeded); case SFileMpqRawChunkSize: if(pHeader->dwRawChunkSize == 0) return GetInfo_ReturdwErrCode(ERROR_FILE_NOT_FOUND); return GetInfo(pvFileInfo, cbFileInfo, &pHeader->dwRawChunkSize, sizeof(DWORD), pcbLengthNeeded); case SFileMpqStreamFlags: FileStream_GetFlags(ha->pStream, &dwInt32Value); return GetInfo(pvFileInfo, cbFileInfo, &dwInt32Value, sizeof(DWORD), pcbLengthNeeded); case SFileMpqFlags: return GetInfo(pvFileInfo, cbFileInfo, &ha->dwFlags, sizeof(DWORD), pcbLengthNeeded); case SFileInfoPatchChain: return GetInfo_PatchChain(hf, pvFileInfo, cbFileInfo, pcbLengthNeeded); case SFileInfoFileEntry: if(pFileEntry == NULL) return GetInfo_ReturdwErrCode(ERROR_FILE_NOT_FOUND); return GetInfo_FileEntry(pvFileInfo, cbFileInfo, pFileEntry, pcbLengthNeeded); case SFileInfoHashEntry: return GetInfo(pvFileInfo, cbFileInfo, hf->pHashEntry, sizeof(TMPQHash), pcbLengthNeeded); case SFileInfoHashIndex: return GetInfo(pvFileInfo, cbFileInfo, &hf->dwHashIndex, sizeof(DWORD), pcbLengthNeeded); case SFileInfoNameHash1: return GetInfo(pvFileInfo, cbFileInfo, &hf->pHashEntry->dwName1, sizeof(DWORD), pcbLengthNeeded); case SFileInfoNameHash2: return GetInfo(pvFileInfo, cbFileInfo, &hf->pHashEntry->dwName2, sizeof(DWORD), pcbLengthNeeded); case SFileInfoNameHash3: return GetInfo(pvFileInfo, cbFileInfo, &pFileEntry->FileNameHash, sizeof(ULONGLONG), pcbLengthNeeded); case SFileInfoLocale: dwInt32Value = hf->pHashEntry->lcLocale; return GetInfo(pvFileInfo, cbFileInfo, &dwInt32Value, sizeof(DWORD), pcbLengthNeeded); case SFileInfoFileIndex: dwInt32Value = (DWORD)(pFileEntry - hf->ha->pFileTable); return GetInfo(pvFileInfo, cbFileInfo, &dwInt32Value, sizeof(DWORD), pcbLengthNeeded); case SFileInfoByteOffset: return GetInfo(pvFileInfo, cbFileInfo, &pFileEntry->ByteOffset, sizeof(ULONGLONG), pcbLengthNeeded); case SFileInfoFileTime: return GetInfo(pvFileInfo, cbFileInfo, &pFileEntry->FileTime, sizeof(ULONGLONG), pcbLengthNeeded); case SFileInfoFileSize: return GetInfo(pvFileInfo, cbFileInfo, &pFileEntry->dwFileSize, sizeof(DWORD), pcbLengthNeeded); case SFileInfoCompressedSize: return GetInfo(pvFileInfo, cbFileInfo, &pFileEntry->dwCmpSize, sizeof(DWORD), pcbLengthNeeded); case SFileInfoFlags: return GetInfo(pvFileInfo, cbFileInfo, &pFileEntry->dwFlags, sizeof(DWORD), pcbLengthNeeded); case SFileInfoEncryptionKey: return GetInfo(pvFileInfo, cbFileInfo, &hf->dwFileKey, sizeof(DWORD), pcbLengthNeeded); case SFileInfoEncryptionKeyRaw: dwInt32Value = hf->dwFileKey; if(pFileEntry->dwFlags & MPQ_FILE_FIX_KEY) dwInt32Value = (dwInt32Value ^ pFileEntry->dwFileSize) - (DWORD)hf->MpqFilePos; return GetInfo(pvFileInfo, cbFileInfo, &dwInt32Value, sizeof(DWORD), pcbLengthNeeded); case SFileInfoCRC32: return GetInfo(pvFileInfo, cbFileInfo, &hf->pFileEntry->dwCrc32, sizeof(DWORD), pcbLengthNeeded); } // Invalid info class return GetInfo_ReturdwErrCode(ERROR_INVALID_PARAMETER); } bool WINAPI SFileFreeFileInfo(void * pvFileInfo, SFileInfoClass InfoClass) { switch(InfoClass) { case SFileMpqHetTable: FreeHetTable((TMPQHetTable *)pvFileInfo); return true; case SFileMpqBetTable: FreeBetTable((TMPQBetTable *)pvFileInfo); return true; default: break; } 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 '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_CAN_NOT_COMPLETE; } bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName) { TMPQFile * hf = (TMPQFile *)hFile; // MPQ File handle DWORD dwErrCode = ERROR_INVALID_HANDLE; // 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) dwErrCode = CreatePseudoFileName(hFile, pFileEntry, szFileName); // Copy the file name to the output buffer, if any if(pFileEntry->szFileName && szFileName) { strcpy(szFileName, pFileEntry->szFileName); dwErrCode = ERROR_SUCCESS; } } } // For local files, copy the file name from the stream else { if(szFileName != NULL) { const TCHAR * szStreamName = FileStream_GetFileName(hf->pStream); StringCopy(szFileName, MAX_PATH, szStreamName); } dwErrCode = ERROR_SUCCESS; } } if(dwErrCode != ERROR_SUCCESS) SetLastError(dwErrCode); return (dwErrCode == ERROR_SUCCESS); }