// This function gets the right positions of the hash table and the block table. static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize) { TMPQHeader * pHeader = ha->pHeader; ULONGLONG ByteOffset; // Check the begin of HET table if(pHeader->HetTablePos64) { ByteOffset = ha->MpqPos + pHeader->HetTablePos64; if(ByteOffset > FileSize) return ERROR_BAD_FORMAT; } // Check the begin of BET table if(pHeader->BetTablePos64) { ByteOffset = ha->MpqPos + pHeader->BetTablePos64; if(ByteOffset > FileSize) return ERROR_BAD_FORMAT; } // Check the begin of hash table if(pHeader->wHashTablePosHi || pHeader->dwHashTablePos) { ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos); if(ByteOffset > FileSize) return ERROR_BAD_FORMAT; } // Check the begin of block table if(pHeader->wBlockTablePosHi || pHeader->dwBlockTablePos) { if((pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) && (ha->dwFlags & MPQ_FLAG_MALFORMED)) { ByteOffset = (DWORD)ha->MpqPos + pHeader->dwBlockTablePos; if(ByteOffset > FileSize) return ERROR_BAD_FORMAT; } else { ByteOffset = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); if(ByteOffset > FileSize) return ERROR_BAD_FORMAT; } } // Check the begin of hi-block table if(pHeader->HiBlockTablePos64 != 0) { ByteOffset = ha->MpqPos + pHeader->HiBlockTablePos64; if(ByteOffset > FileSize) return ERROR_BAD_FORMAT; } // All OK. return ERROR_SUCCESS; }
// Calculate begin and end of the MPQ archive static void CalculateArchiveRange( TMPQArchive * ha, PMPQ_SIGNATURE_INFO pSI) { TMPQHeader * pHeader = ha->pHeader; ULONGLONG TempPos = 0; ULONGLONG MaxPos; char szMapHeader[0x200]; // Get the MPQ begin pSI->BeginMpqData = ha->MpqPos; // Warcraft III maps are signed from the map header to the end if(FileStream_Read(ha->pStream, &TempPos, szMapHeader, sizeof(szMapHeader))) { // Is it a map header ? if(szMapHeader[0] == 'H' && szMapHeader[1] == 'M' && szMapHeader[2] == '3' && szMapHeader[3] == 'W') { // We will have to hash since the map header pSI->BeginMpqData = 0; } } // Get the MPQ data end. The end is calculated as the biggest // value of (end of the last file), (end of block table), // (end of ext block table), (end of hash table) FindFreeMpqSpace(ha, &MaxPos); // Check if hash table is beyond TempPos = ha->MpqPos + MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos) + pHeader->HashTableSize64; if(TempPos > MaxPos) MaxPos = TempPos; // Check if block table is beyond TempPos = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos) + pHeader->BlockTableSize64; if(TempPos > MaxPos) MaxPos = TempPos; // Check if ext block table is beyond if(pHeader->HiBlockTablePos64 != 0) { TempPos = ha->MpqPos + pHeader->HiBlockTablePos64 + pHeader->HiBlockTableSize64; if(TempPos > MaxPos) MaxPos = TempPos; } // Give the end pSI->EndMpqData = MaxPos; // Get the size of the entire file FileStream_GetSize(ha->pStream, pSI->EndOfFile); }
// This function gets the right positions of the hash table and the block table. static int VerifyMpqTablePositions(TMPQArchive * ha, ULONGLONG FileSize) { TMPQHeader * pHeader = ha->pHeader; ULONGLONG ByteOffset; // Check the begin of HET table if(pHeader->HetTablePos64) { ByteOffset = ha->MpqPos + pHeader->HetTablePos64; if(ByteOffset > FileSize) return ERROR_BAD_FORMAT; } // Check the begin of BET table if(pHeader->BetTablePos64) { ByteOffset = ha->MpqPos + pHeader->BetTablePos64; if(ByteOffset > FileSize) return ERROR_BAD_FORMAT; } // Check the begin of hash table if(pHeader->wHashTablePosHi || pHeader->dwHashTablePos) { ByteOffset = FileOffsetFromMpqOffset(ha, MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos)); if(ByteOffset > FileSize) return ERROR_BAD_FORMAT; } // Check the begin of block table if(pHeader->wBlockTablePosHi || pHeader->dwBlockTablePos) { ByteOffset = FileOffsetFromMpqOffset(ha, MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos)); if(ByteOffset > FileSize) return ERROR_BAD_FORMAT; } // Check the begin of hi-block table if(pHeader->HiBlockTablePos64 != 0) { ByteOffset = ha->MpqPos + pHeader->HiBlockTablePos64; if(ByteOffset > FileSize) return ERROR_BAD_FORMAT; } // All OK. return ERROR_SUCCESS; }
DWORD WINAPI SFileSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod) { TMPQFile * hf = (TMPQFile *)hFile; ULONGLONG FilePosition; ULONGLONG MoveOffset; ULONGLONG FileSize; 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 = hf->dwDataSize; } break; default: SetLastError(ERROR_INVALID_PARAMETER); return SFILE_INVALID_POS; } // Get the current file size if(hf->pStream != NULL) { FileStream_GetSize(hf->pStream, FileSize); } else { FileSize = hf->dwDataSize; } // 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)
DWORD WINAPI CascSetFilePointer(HANDLE hFile, LONG lFilePos, LONG * plFilePosHigh, DWORD dwMoveMethod) { TCascFile * hf; ULONGLONG FilePosition; ULONGLONG MoveOffset; DWORD dwFilePosHi; // If the hFile is not a valid file handle, return an error. hf = IsValidFileHandle(hFile); if(hf == NULL) { SetLastError(ERROR_INVALID_HANDLE); return CASC_INVALID_POS; } // Get the relative point where to move from switch(dwMoveMethod) { case FILE_BEGIN: FilePosition = 0; break; case FILE_CURRENT: FilePosition = hf->FilePointer; break; case FILE_END: FilePosition = hf->FileSize; break; default: SetLastError(ERROR_INVALID_PARAMETER); return CASC_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 overflow FilePosition = ((FilePosition + MoveOffset) >= FilePosition) ? (FilePosition + MoveOffset) : 0; // CASC files can't be bigger than 4 GB. // We don't allow to go past 4 GB if(FilePosition >> 32) { SetLastError(ERROR_INVALID_PARAMETER); return CASC_INVALID_POS; } // Change the file position hf->FilePointer = (DWORD)FilePosition; // Return the new file position if(plFilePosHigh != NULL) *plFilePosHigh = 0; return hf->FilePointer; }
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; TMPQHash * pHash; TMPQFile * hf = NULL; void * pvSrcFileInfo = NULL; DWORD cbSrcFileInfo = 0; DWORD dwInt32Value = 0; int nInfoType = SFILE_INFO_TYPE_INVALID_HANDLE; int nError = ERROR_SUCCESS; switch(InfoClass) { case SFileMpqFileName: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = (void *)FileStream_GetFileName(ha->pStream); cbSrcFileInfo = (DWORD)(_tcslen((TCHAR *)pvSrcFileInfo) + 1) * sizeof(TCHAR); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqStreamBitmap: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) return FileStream_GetBitmap(ha->pStream, pvFileInfo, cbFileInfo, pcbLengthNeeded); break; case SFileMpqUserDataOffset: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; if(ha->pUserData != NULL) { pvSrcFileInfo = &ha->UserDataPos; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } } break; case SFileMpqUserDataHeader: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; if(ha->pUserData != NULL) { ByteOffset = ha->UserDataPos; cbSrcFileInfo = sizeof(TMPQUserData); nInfoType = SFILE_INFO_TYPE_READ_FROM_FILE; } } break; case SFileMpqUserData: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; if(ha->pUserData != NULL) { ByteOffset = ha->UserDataPos + sizeof(TMPQUserData); cbSrcFileInfo = ha->pUserData->dwHeaderOffs - sizeof(TMPQUserData); nInfoType = SFILE_INFO_TYPE_READ_FROM_FILE; } } break; case SFileMpqHeaderOffset: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->MpqPos; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqHeaderSize: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->dwHeaderSize; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqHeader: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { ByteOffset = ha->MpqPos; cbSrcFileInfo = ha->pHeader->dwHeaderSize; nInfoType = SFILE_INFO_TYPE_READ_FROM_FILE; } break; case SFileMpqHetTableOffset: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->HetTablePos64; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqHetTableSize: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->HetTableSize64; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqHetHeader: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; pvSrcFileInfo = LoadExtTable(ha, ha->pHeader->HetTablePos64, (size_t)ha->pHeader->HetTableSize64, HET_TABLE_SIGNATURE, MPQ_KEY_HASH_TABLE); if(pvSrcFileInfo != NULL) { cbSrcFileInfo = sizeof(TMPQHetHeader); nInfoType = SFILE_INFO_TYPE_ALLOCATED; } } break; case SFileMpqHetTable: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; pvSrcFileInfo = LoadHetTable(ha); if(pvSrcFileInfo != NULL) { cbSrcFileInfo = sizeof(void *); nInfoType = SFILE_INFO_TYPE_TABLE_POINTER; } } break; case SFileMpqBetTableOffset: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->BetTablePos64; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqBetTableSize: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->BetTableSize64; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqBetHeader: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; 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; case SFileMpqBetTable: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; pvSrcFileInfo = LoadBetTable(ha); if(pvSrcFileInfo != NULL) { cbSrcFileInfo = sizeof(void *); nInfoType = SFILE_INFO_TYPE_TABLE_POINTER; } } break; 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; case SFileMpqHashTableSize64: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->HashTableSize64; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqHashTableSize: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->dwHashTableSize; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqHashTable: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL && ha->pHashTable != NULL) { pvSrcFileInfo = ha->pHashTable; cbSrcFileInfo = ha->dwHashTableSize * sizeof(TMPQHash); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqBlockTableOffset: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { Int64Value = MAKE_OFFSET64(ha->pHeader->wBlockTablePosHi, ha->pHeader->dwBlockTablePos); pvSrcFileInfo = &Int64Value; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqBlockTableSize64: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->BlockTableSize64; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqBlockTableSize: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->dwBlockTableSize; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqBlockTable: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; if(MAKE_OFFSET64(ha->pHeader->wBlockTablePosHi, ha->pHeader->dwBlockTablePos) != 0) { cbSrcFileInfo = ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock); if(cbFileInfo >= cbSrcFileInfo) pvSrcFileInfo = LoadBlockTable(ha, true); nInfoType = SFILE_INFO_TYPE_ALLOCATED; } } break; case SFileMpqHiBlockTableOffset: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->HiBlockTablePos64; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqHiBlockTableSize64: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->HiBlockTableSize64; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqHiBlockTable: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; if(ha->pHeader->HiBlockTablePos64 && ha->pHeader->HiBlockTableSize64) { assert(false); } } break; case SFileMpqSignatures: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL && QueryMpqSignatureInfo(ha, &SignatureInfo)) { pvSrcFileInfo = &SignatureInfo.SignatureTypes; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqStrongSignatureOffset: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; if(QueryMpqSignatureInfo(ha, &SignatureInfo) && (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG)) { pvSrcFileInfo = &SignatureInfo.EndMpqData; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } } break; case SFileMpqStrongSignatureSize: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; if(QueryMpqSignatureInfo(ha, &SignatureInfo) && (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG)) { dwInt32Value = MPQ_STRONG_SIGNATURE_SIZE + 4; pvSrcFileInfo = &dwInt32Value; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } } break; case SFileMpqStrongSignature: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; if(QueryMpqSignatureInfo(ha, &SignatureInfo) && (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG)) { pvSrcFileInfo = SignatureInfo.Signature; cbSrcFileInfo = MPQ_STRONG_SIGNATURE_SIZE + 4; nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } } break; case SFileMpqArchiveSize64: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->ArchiveSize64; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqArchiveSize: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->dwArchiveSize; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqMaxFileCount: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->dwMaxFileCount; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqFileTableSize: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->dwFileTableSize; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqSectorSize: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->dwSectorSize; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; 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) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; if(ha->pHeader->dwRawChunkSize != 0) { pvSrcFileInfo = &ha->pHeader->dwRawChunkSize; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } } break; 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 SFileMpqFlags: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { dwInt32Value = ha->dwFlags; pvSrcFileInfo = &dwInt32Value; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; 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 && hf->pFileEntry != NULL) { pvSrcFileInfo = &hf->pFileEntry->dwHashIndex; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileInfoNameHash1: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->ha != NULL && hf->ha->pHashTable != NULL) { pHash = hf->ha->pHashTable + hf->pFileEntry->dwHashIndex; pvSrcFileInfo = &pHash->dwName1; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileInfoNameHash2: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->ha != NULL && hf->ha->pHashTable != NULL) { pHash = hf->ha->pHashTable + hf->pFileEntry->dwHashIndex; pvSrcFileInfo = &pHash->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 SFileInfoLocale: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->pFileEntry != NULL) { dwInt32Value = hf->pFileEntry->lcLocale; pvSrcFileInfo = &dwInt32Value; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileInfoFileIndex: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->ha != NULL && hf->pFileEntry != NULL) { dwInt32Value = (DWORD)(hf->pFileEntry - hf->ha->pFileTable); pvSrcFileInfo = &dwInt32Value; cbSrcFileInfo = sizeof(DWORD); 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 SFileInfoFileTime: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->pFileEntry != NULL) { pvSrcFileInfo = &hf->pFileEntry->FileTime; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileInfoFileSize: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->pFileEntry != NULL) { pvSrcFileInfo = &hf->pFileEntry->dwFileSize; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileInfoCompressedSize: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->pFileEntry != NULL) { pvSrcFileInfo = &hf->pFileEntry->dwCmpSize; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileInfoFlags: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->pFileEntry != NULL) { pvSrcFileInfo = &hf->pFileEntry->dwFlags; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileInfoEncryptionKey: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL) { pvSrcFileInfo = &hf->dwFileKey; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileInfoEncryptionKeyRaw: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->pFileEntry != NULL) { dwInt32Value = hf->dwFileKey; if(hf->pFileEntry->dwFlags & MPQ_FILE_FIX_KEY) dwInt32Value = (dwInt32Value ^ hf->pFileEntry->dwFileSize) - (DWORD)hf->MpqFilePos; pvSrcFileInfo = &dwInt32Value; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; default: // Invalid info class SetLastError(ERROR_INVALID_PARAMETER); return false; } // If we validated the handle and info class, give as much info as possible if(nInfoType >= SFILE_INFO_TYPE_DIRECT_POINTER) { // Give the length needed, if wanted if(pcbLengthNeeded != NULL) pcbLengthNeeded[0] = cbSrcFileInfo; // If the caller entered an output buffer, the output size must also be entered if(pvFileInfo != NULL && cbFileInfo != 0) { // 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: assert(pvSrcFileInfo != NULL); memcpy(pvFileInfo, pvSrcFileInfo, cbSrcFileInfo); break; case SFILE_INFO_TYPE_READ_FROM_FILE: if(!FileStream_Read(ha->pStream, &ByteOffset, pvFileInfo, cbSrcFileInfo)) nError = GetLastError(); break; case SFILE_INFO_TYPE_TABLE_POINTER: assert(pvSrcFileInfo != NULL); *(void **)pvFileInfo = pvSrcFileInfo; pvSrcFileInfo = NULL; break; case SFILE_INFO_TYPE_FILE_ENTRY: assert(pFileEntry != NULL); ConvertFileEntryToSelfRelative((TFileEntry *)pvFileInfo, pFileEntry); break; } } else { nError = ERROR_INSUFFICIENT_BUFFER; } } // 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); } else { // Handle error cases if(nInfoType == SFILE_INFO_TYPE_INVALID_HANDLE) nError = ERROR_INVALID_HANDLE; if(nInfoType == SFILE_INFO_TYPE_NOT_FOUND) nError = ERROR_FILE_NOT_FOUND; } // Set the last error value, if needed if(nError != ERROR_SUCCESS) SetLastError(nError); return (nError == ERROR_SUCCESS); }
void ConvertMpqHeaderToFormat4( TMPQArchive * ha, ULONGLONG FileSize, DWORD dwFlags) { ULONGLONG ByteOffset; TMPQHeader * pHeader = ha->pHeader; DWORD dwExpectedArchiveSize; USHORT wFormatVersion = pHeader->wFormatVersion; // If version 1.0 is forced, then the format version is forced to be 1.0 // Reason: Storm.dll in Warcraft III ignores format version value if(dwFlags & MPQ_OPEN_FORCE_MPQ_V1) wFormatVersion = MPQ_FORMAT_VERSION_1; // Format-specific fixes switch(wFormatVersion) { case MPQ_FORMAT_VERSION_1: // Check for malformed MPQ header version 1.0 if(pHeader->dwHeaderSize != MPQ_HEADER_SIZE_V1) { pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V1; ha->dwFlags |= MPQ_FLAG_PROTECTED; } // // The value of "dwArchiveSize" member in the MPQ header // is ignored by Storm.dll and can contain garbage value // ("w3xmaster" protector). // dwExpectedArchiveSize = (DWORD)(FileSize - ha->MpqPos); if(pHeader->dwArchiveSize != dwExpectedArchiveSize) { // Note: dwExpectedArchiveSize might be incorrect at this point. // MPQs version 1.0 can have strong digital signature appended at the end, // or they might just have arbitrary data there. // In either case, we recalculate the archive size later when // block table is loaded and positions of all files is known. pHeader->dwArchiveSize = dwExpectedArchiveSize; ha->dwFlags |= MPQ_FLAG_NEED_FIX_SIZE; } // Zero the fields in 2.0 part of the MPQ header pHeader->HiBlockTablePos64 = 0; pHeader->wHashTablePosHi = 0; pHeader->wBlockTablePosHi = 0; // No break here !!! case MPQ_FORMAT_VERSION_2: case MPQ_FORMAT_VERSION_3: // In MPQ format 3.0, the entire header is optional // and the size of the header can actually be identical // to size of header 2.0 if(pHeader->dwHeaderSize < MPQ_HEADER_SIZE_V3) { ULONGLONG ArchiveSize64 = pHeader->dwArchiveSize; // In format 2.0, the archive size is obsolete and is calculated // as the highest offset of hash table, block table or hi-block table. // However, we can still rely on it, if the size of the archive is under 4 GB if((FileSize - ha->MpqPos) >> 32) { ByteOffset = MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos) + (pHeader->dwHashTableSize * sizeof(TMPQHash)); if(ByteOffset > ArchiveSize64) ArchiveSize64 = ByteOffset; ByteOffset = MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos) + (pHeader->dwBlockTableSize * sizeof(TMPQBlock)); if(ByteOffset > ArchiveSize64) ArchiveSize64 = ByteOffset; // Only if we actually have a hi-block table if(pHeader->HiBlockTablePos64) { ByteOffset = pHeader->HiBlockTablePos64 + (pHeader->dwBlockTableSize * sizeof(USHORT)); if(ByteOffset > ArchiveSize64) ArchiveSize64 = ByteOffset; } // We need to recalculate archive size later, // when block table is loaded and the position of files is known ha->dwFlags |= MPQ_FLAG_NEED_FIX_SIZE; } // Initialize the rest of the 3.0 header pHeader->ArchiveSize64 = ArchiveSize64; pHeader->HetTablePos64 = 0; pHeader->BetTablePos64 = 0; } // // Calculate compressed size of each table. We assume the following order: // 1) HET table // 2) BET table // 3) Classic hash table // 4) Classic block table // 5) Hi-block table // // Set all sizes to zero pHeader->HetTableSize64 = 0; pHeader->BetTableSize64 = 0; // Either both HET and BET table exist or none of them does. if(pHeader->HetTablePos64) { // Compressed size of the HET and BET tables pHeader->HetTableSize64 = pHeader->BetTablePos64 - pHeader->HetTablePos64; pHeader->BetTableSize64 = MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos) - pHeader->HetTablePos64; } // Compressed size of hash and block table if(wFormatVersion >= MPQ_FORMAT_VERSION_3) { // Compressed size of the hash table pHeader->HashTableSize64 = MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos) - MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos); // Block and hi-block table if(pHeader->HiBlockTablePos64) { pHeader->BlockTableSize64 = pHeader->HiBlockTablePos64 - MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); pHeader->HiBlockTableSize64 = pHeader->ArchiveSize64 - pHeader->HiBlockTablePos64; } else { pHeader->BlockTableSize64 = pHeader->ArchiveSize64 - MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos); pHeader->HiBlockTableSize64 = 0; } } else { // No known MPQ in format 1.0 has any of the tables compressed pHeader->HashTableSize64 = pHeader->dwHashTableSize * sizeof(TMPQHash); pHeader->BlockTableSize64 = pHeader->dwBlockTableSize * sizeof(TMPQBlock); pHeader->HiBlockTableSize64 = 0; } // Set the data chunk size for MD5 to zero pHeader->dwRawChunkSize = 0; // Fill the MD5's memset(pHeader->MD5_BlockTable, 0, MD5_DIGEST_SIZE); memset(pHeader->MD5_HashTable, 0, MD5_DIGEST_SIZE); memset(pHeader->MD5_HiBlockTable, 0, MD5_DIGEST_SIZE); memset(pHeader->MD5_UnknownA0, 0, MD5_DIGEST_SIZE); memset(pHeader->MD5_UnknownB0, 0, MD5_DIGEST_SIZE); memset(pHeader->MD5_MpqHeader, 0, MD5_DIGEST_SIZE); // No break here !!!! case MPQ_FORMAT_VERSION_4: // Verify header MD5. Header MD5 is calculated from the MPQ header since the 'MPQ\x1A' // signature until the position of header MD5 at offset 0xC0 // if(dwHeaderSize >= MPQ_HEADER_SIZE_V4) // wow_SFileVerifyMpqHeaderMD5(ha->pHeader); break; }