static int CheckIfAllFilesKnown(TMPQArchive * ha, const char * szListFile, LPDWORD pFileKeys) { TFileEntry * pFileTableEnd; TFileEntry * pFileEntry; DWORD dwBlockIndex = 0; int nError = ERROR_SUCCESS; // Add the listfile to the MPQ if (nError == ERROR_SUCCESS && szListFile != NULL) { // Notify the user if (CompactCB != NULL) CompactCB(pvUserData, CCB_CHECKING_FILES, CompactBytesProcessed, CompactTotalBytes); if (!SFileAddListFile((HANDLE)ha, szListFile)) nError = ERROR_CAN_NOT_COMPLETE; } // Verify the file table if (nError == ERROR_SUCCESS) { pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++, dwBlockIndex++) { if (pFileEntry->dwFlags & MPQ_FILE_EXISTS) { if (pFileEntry->szFileName != NULL && !IsPseudoFileName(pFileEntry->szFileName, NULL)) { DWORD dwFileKey = 0; // Resolve the file key. Use plain file name for it if (pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) { dwFileKey = DecryptFileKey(pFileEntry->szFileName, pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags); } // Give the key to the caller if (pFileKeys != NULL) pFileKeys[dwBlockIndex] = dwFileKey; } else { nError = ERROR_CAN_NOT_COMPLETE; break; } } } } return nError; }
static int CheckIfAllKeysKnown(TMPQArchive * ha, const TCHAR * szListFile, LPDWORD pFileKeys) { TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; TFileEntry * pFileEntry; DWORD dwBlockIndex = 0; int nError = ERROR_SUCCESS; // Add the listfile to the MPQ if(szListFile != NULL) { // Notify the user if(ha->pfnCompactCB != NULL) ha->pfnCompactCB(ha->pvCompactUserData, CCB_CHECKING_FILES, ha->CompactBytesProcessed, ha->CompactTotalBytes); nError = SFileAddListFile((HANDLE)ha, szListFile); } // Verify the file table if(nError == ERROR_SUCCESS) { for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++, dwBlockIndex++) { // If the file exists and it's encrypted if(pFileEntry->dwFlags & MPQ_FILE_EXISTS) { // If we know the name, we decrypt the file key from the file name if(pFileEntry->szFileName != NULL && !IsPseudoFileName(pFileEntry->szFileName, NULL)) { // Give the key to the caller pFileKeys[dwBlockIndex] = DecryptFileKey(pFileEntry->szFileName, pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags); continue; } // We don't know the encryption key of this file, // thus we cannot compact the file nError = ERROR_UNKNOWN_FILE_NAMES; break; } } } return nError; }
bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * phFile) { TMPQArchive * ha = (TMPQArchive *)hMpq; TFileEntry * pFileEntry = NULL; TMPQFile * hf = NULL; DWORD dwBlockIndex = 0; // Found table index int nError = ERROR_SUCCESS; // Don't accept NULL pointer to file handle if (phFile == NULL) nError = ERROR_INVALID_PARAMETER; // Prepare the file opening if (nError == ERROR_SUCCESS) { switch(dwSearchScope) { case SFILE_OPEN_PATCHED_FILE: // We want to open the updated version of the file return OpenPatchedFile(hMpq, szFileName, 0, phFile); case SFILE_OPEN_FROM_MPQ: if (!IsValidMpqHandle(ha)) { nError = ERROR_INVALID_HANDLE; break; } if (szFileName == NULL || *szFileName == 0) { nError = ERROR_INVALID_PARAMETER; break; } // First of all, check the name as-is pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale); if (pFileEntry != NULL) break; // If the file doesn't exist in the MPQ, check file pseudo-name ("FileXXXXXXXX.ext") if (!IsPseudoFileName(szFileName, &dwBlockIndex)) { nError = ERROR_FILE_NOT_FOUND; break; } // Set the file name to the file index and fall through szFileName = (const char *)(DWORD_PTR)dwBlockIndex; dwSearchScope = SFILE_OPEN_BY_INDEX; // No break here, fall through. case SFILE_OPEN_BY_INDEX: if (!IsValidMpqHandle(ha)) { nError = ERROR_INVALID_HANDLE; break; } // Set handle size to be sizeof(TMPQFile) + length of FileXXXXXXXX.xxx pFileEntry = GetFileEntryByIndex(ha, (DWORD)(DWORD_PTR)szFileName); if (pFileEntry == NULL) nError = ERROR_FILE_NOT_FOUND; break; case SFILE_OPEN_ANY_LOCALE: // This open option is reserved for opening MPQ internal listfile. // No argument validation. Tries to open file with neutral locale first, // then any other available. dwSearchScope = SFILE_OPEN_FROM_MPQ; pFileEntry = GetFileEntryAny(ha, szFileName); if (pFileEntry == NULL) nError = ERROR_FILE_NOT_FOUND; break; case SFILE_OPEN_LOCAL_FILE: if (szFileName == NULL || *szFileName == 0) { nError = ERROR_INVALID_PARAMETER; break; } return OpenLocalFile(szFileName, phFile); default: // Don't accept any other value nError = ERROR_INVALID_PARAMETER; break; } // Quick return if something failed if (nError != ERROR_SUCCESS) { SetLastError(nError); return false; } } // Test if the file was not already deleted. if (nError == ERROR_SUCCESS) { if ((pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0) nError = ERROR_FILE_NOT_FOUND; if (pFileEntry->dwFlags & ~MPQ_FILE_VALID_FLAGS) nError = ERROR_NOT_SUPPORTED; } // Allocate file handle if (nError == ERROR_SUCCESS) { if ((hf = ALLOCMEM(TMPQFile, 1)) == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; } // Initialize file handle if (nError == ERROR_SUCCESS) { memset(hf, 0, sizeof(TMPQFile)); hf->pFileEntry = pFileEntry; hf->dwMagic = ID_MPQ_FILE; hf->ha = ha; hf->MpqFilePos = pFileEntry->ByteOffset; hf->RawFilePos = ha->MpqPos + hf->MpqFilePos; hf->dwDataSize = pFileEntry->dwFileSize; hf->dwHashIndex = pFileEntry->dwHashIndex; hf->dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable); // If the MPQ has sector CRC enabled, enable if for the file if (ha->dwFlags & MPQ_FLAG_CHECK_SECTOR_CRC) hf->bCheckSectorCRCs = true; // Decrypt file key. Cannot be used if the file is given by index if (dwSearchScope == SFILE_OPEN_FROM_MPQ) { if (pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) { hf->dwFileKey = DecryptFileKey(szFileName, pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags); } } else { // If the file is encrypted and not compressed, we cannot detect the file key if (!SFileGetFileName(hf, NULL)) nError = GetLastError(); } } // If the file is actually a patch file, we have to load the patch file header if (nError == ERROR_SUCCESS && pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) { assert(hf->PatchInfo == NULL); nError = AllocatePatchInfo(hf, true); } // Cleanup if (nError != ERROR_SUCCESS) { SetLastError(nError); FreeMPQFile(hf); } *phFile = hf; return (nError == ERROR_SUCCESS); }
static int RecryptFileData( TMPQArchive * ha, TMPQFile * hf, const char * szFileName, const char * szNewFileName) { ULONGLONG RawFilePos; TFileEntry * pFileEntry = hf->pFileEntry; DWORD dwBytesToRecrypt = pFileEntry->dwCmpSize; DWORD dwOldKey; DWORD dwNewKey; int nError = ERROR_SUCCESS; // The file must be encrypted assert(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED); // File decryption key is calculated from the plain name szNewFileName = GetPlainFileNameA(szNewFileName); szFileName = GetPlainFileNameA(szFileName); // Calculate both file keys dwOldKey = DecryptFileKey(szFileName, pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags); dwNewKey = DecryptFileKey(szNewFileName, pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags); // Incase the keys are equal, don't recrypt the file if(dwNewKey == dwOldKey) return ERROR_SUCCESS; hf->dwFileKey = dwOldKey; // Calculate the raw position of the file in the archive hf->MpqFilePos = pFileEntry->ByteOffset; hf->RawFilePos = ha->MpqPos + hf->MpqFilePos; // Allocate buffer for file transfer nError = AllocateSectorBuffer(hf); if(nError != ERROR_SUCCESS) return nError; // Also allocate buffer for sector offsets // Note: Don't load sector checksums, we don't need to recrypt them nError = AllocateSectorOffsets(hf, true); if(nError != ERROR_SUCCESS) return nError; // If we have sector offsets, recrypt these as well if(hf->SectorOffsets != NULL) { // Allocate secondary buffer for sectors copy DWORD * SectorOffsetsCopy = (DWORD *)STORM_ALLOC(BYTE, hf->SectorOffsets[0]); DWORD dwSectorOffsLen = hf->SectorOffsets[0]; if(SectorOffsetsCopy == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Recrypt the array of sector offsets memcpy(SectorOffsetsCopy, hf->SectorOffsets, dwSectorOffsLen); EncryptMpqBlock(SectorOffsetsCopy, dwSectorOffsLen, dwNewKey - 1); BSWAP_ARRAY32_UNSIGNED(SectorOffsetsCopy, dwSectorOffsLen); // Write the recrypted array back if(!FileStream_Write(ha->pStream, &hf->RawFilePos, SectorOffsetsCopy, dwSectorOffsLen)) nError = GetLastError(); STORM_FREE(SectorOffsetsCopy); } // Now we have to recrypt all file sectors. We do it without // recompression, because recompression is not necessary in this case if(nError == ERROR_SUCCESS) { for(DWORD dwSector = 0; dwSector < hf->dwSectorCount; dwSector++) { DWORD dwRawDataInSector = hf->dwSectorSize; DWORD dwRawByteOffset = dwSector * hf->dwSectorSize; // Last sector: If there is not enough bytes remaining in the file, cut the raw size if(dwRawDataInSector > dwBytesToRecrypt) dwRawDataInSector = dwBytesToRecrypt; // Fix the raw data length if the file is compressed if(hf->SectorOffsets != NULL) { dwRawDataInSector = hf->SectorOffsets[dwSector+1] - hf->SectorOffsets[dwSector]; dwRawByteOffset = hf->SectorOffsets[dwSector]; } // Calculate the raw file offset of the file sector CalculateRawSectorOffset(RawFilePos, hf, dwRawByteOffset); // Read the file sector if(!FileStream_Read(ha->pStream, &RawFilePos, hf->pbFileSector, dwRawDataInSector)) { nError = GetLastError(); break; } // If necessary, re-encrypt the sector // Note: Recompression is not necessary here. Unlike encryption, // the compression does not depend on the position of the file in MPQ. BSWAP_ARRAY32_UNSIGNED(hf->pbFileSector, dwRawDataInSector); DecryptMpqBlock(hf->pbFileSector, dwRawDataInSector, dwOldKey + dwSector); EncryptMpqBlock(hf->pbFileSector, dwRawDataInSector, dwNewKey + dwSector); BSWAP_ARRAY32_UNSIGNED(hf->pbFileSector, dwRawDataInSector); // Write the sector back if(!FileStream_Write(ha->pStream, &RawFilePos, hf->pbFileSector, dwRawDataInSector)) { nError = GetLastError(); break; } // Decrement number of bytes remaining dwBytesToRecrypt -= hf->dwSectorSize; } } return nError; }
bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * phFile) { TMPQArchive * ha = (TMPQArchive *)hMpq; TFileEntry * pFileEntry = NULL; TMPQFile * hf = NULL; DWORD dwFileIndex = 0; bool bOpenByIndex = false; int nError = ERROR_SUCCESS; // Don't accept NULL pointer to file handle if(phFile == NULL) nError = ERROR_INVALID_PARAMETER; // Prepare the file opening if(nError == ERROR_SUCCESS) { switch(dwSearchScope) { case SFILE_OPEN_FROM_MPQ: case SFILE_OPEN_BASE_FILE: if(!IsValidMpqHandle(ha)) { nError = ERROR_INVALID_HANDLE; break; } if(szFileName == NULL || *szFileName == 0) { nError = ERROR_INVALID_PARAMETER; break; } // Check the pseudo-file name if(IsPseudoFileName(szFileName, &dwFileIndex)) { pFileEntry = GetFileEntryByIndex(ha, dwFileIndex); bOpenByIndex = true; if(pFileEntry == NULL) nError = ERROR_FILE_NOT_FOUND; } else { // If this MPQ is a patched archive, open the file as patched if(ha->haPatch == NULL || dwSearchScope == SFILE_OPEN_BASE_FILE) { // Otherwise, open the file from *this* MPQ pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale); if(pFileEntry == NULL) nError = ERROR_FILE_NOT_FOUND; } else { return OpenPatchedFile(hMpq, szFileName, 0, phFile); } } break; case SFILE_OPEN_ANY_LOCALE: // This open option is reserved for opening MPQ internal listfile. // No argument validation. Tries to open file with neutral locale first, // then any other available. pFileEntry = GetFileEntryAny(ha, szFileName); if(pFileEntry == NULL) nError = ERROR_FILE_NOT_FOUND; break; case SFILE_OPEN_LOCAL_FILE: if(szFileName == NULL || *szFileName == 0) { nError = ERROR_INVALID_PARAMETER; break; } return OpenLocalFile(szFileName, phFile); default: // Don't accept any other value nError = ERROR_INVALID_PARAMETER; break; } // Quick return if something failed if(nError != ERROR_SUCCESS) { SetLastError(nError); return false; } } // Test if the file was not already deleted. if(nError == ERROR_SUCCESS) { if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0) nError = ERROR_FILE_NOT_FOUND; if(pFileEntry->dwFlags & ~MPQ_FILE_VALID_FLAGS) nError = ERROR_NOT_SUPPORTED; } // Allocate file handle if(nError == ERROR_SUCCESS) { if((hf = STORM_ALLOC(TMPQFile, 1)) == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; } // Initialize file handle if(nError == ERROR_SUCCESS) { memset(hf, 0, sizeof(TMPQFile)); hf->pFileEntry = pFileEntry; hf->dwMagic = ID_MPQ_FILE; hf->ha = ha; hf->MpqFilePos = pFileEntry->ByteOffset; hf->RawFilePos = ha->MpqPos + hf->MpqFilePos; hf->dwDataSize = pFileEntry->dwFileSize; // If the MPQ has sector CRC enabled, enable if for the file if(ha->dwFlags & MPQ_FLAG_CHECK_SECTOR_CRC) hf->bCheckSectorCRCs = true; // If we know the real file name, copy it to the file entry if(bOpenByIndex == false) { // If there is no file name yet, allocate it AllocateFileName(pFileEntry, szFileName); // If the file is encrypted, we should detect the file key if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) { hf->dwFileKey = DecryptFileKey(szFileName, pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags); } } else { // Try to auto-detect the file name if(!SFileGetFileName(hf, NULL)) nError = GetLastError(); } } // If the file is actually a patch file, we have to load the patch file header if(nError == ERROR_SUCCESS && pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) { assert(hf->pPatchInfo == NULL); nError = AllocatePatchInfo(hf, true); } // Cleanup if(nError != ERROR_SUCCESS) { SetLastError(nError); FreeMPQFile(hf); } *phFile = hf; return (nError == ERROR_SUCCESS); }
int SFileAddFile_Init( TMPQArchive * ha, const char * szArchivedName, TMPQFileTime * pFT, DWORD dwFileSize, LCID lcLocale, DWORD dwFlags, TMPQFile ** phf) { LARGE_INTEGER TempPos; // For various file offset calculations TMPQFile * hf = NULL; // File structure for newly added file int nError = ERROR_SUCCESS; // // Note: This is an internal function so no validity checks are done. // It is the caller's responsibility to make sure that no invalid // flags get to this point // // Adjust file flags for too-small files if(dwFileSize < 0x04) dwFlags &= ~(MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY); if(dwFileSize < 0x20) dwFlags &= ~(MPQ_FILE_COMPRESSED | MPQ_FILE_SECTOR_CRC); // Allocate the TMPQFile entry for newly added file hf = CreateMpqFile(ha, szArchivedName); if(hf == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; // If the MPQ header has not yet been written, do it now if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_NO_HEADER)) { // Remember the header size before swapping DWORD dwBytesToWrite = ha->pHeader->dwHeaderSize; BSWAP_TMPQHEADER(ha->pHeader); if(FileStream_Write(ha->pStream, &ha->MpqPos, ha->pHeader, dwBytesToWrite)) ha->dwFlags &= ~MPQ_FLAG_NO_HEADER; else nError = GetLastError(); BSWAP_TMPQHEADER(ha->pHeader); } if(nError == ERROR_SUCCESS) { // Check if the file already exists in the archive if((hf->pHash = GetHashEntryExact(ha, szArchivedName, lcLocale)) != NULL) { if(dwFlags & MPQ_FILE_REPLACEEXISTING) { hf->pBlockEx = ha->pExtBlockTable + hf->pHash->dwBlockIndex; hf->pBlock = ha->pBlockTable + hf->pHash->dwBlockIndex; } else { nError = ERROR_ALREADY_EXISTS; hf->pHash = NULL; } } if(nError == ERROR_SUCCESS && hf->pHash == NULL) { hf->pHash = FindFreeHashEntry(ha, szArchivedName); if(hf->pHash == NULL) { nError = ERROR_DISK_FULL; } } // Set the hash index hf->dwHashIndex = (DWORD)(hf->pHash - ha->pHashTable); hf->bIsWriteHandle = true; } // Find a free space in the MPQ, as well as free block table entry if(nError == ERROR_SUCCESS) { DWORD dwFreeBlock = FindFreeMpqSpace(ha, &hf->MpqFilePos); // Calculate the raw file offset hf->RawFilePos.QuadPart = ha->MpqPos.QuadPart + hf->MpqFilePos.QuadPart; // When format V1, the size of the archive cannot exceed 4 GB if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) { TempPos.QuadPart = hf->MpqFilePos.QuadPart + dwFileSize; TempPos.QuadPart += ha->pHeader->dwHashTableSize * sizeof(TMPQHash); TempPos.QuadPart += ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock); TempPos.QuadPart += ha->pHeader->dwBlockTableSize * sizeof(TMPQBlockEx); if(TempPos.HighPart != 0) nError = ERROR_DISK_FULL; } // If we didn't get a block table entry assigned from hash table, assign it now if(hf->pBlock == NULL) { // Note: dwFreeBlock can be greater than dwHashTableSize, // in case that block table is bigger than hash table if(dwFreeBlock != 0xFFFFFFFF) { hf->pBlockEx = ha->pExtBlockTable + dwFreeBlock; hf->pBlock = ha->pBlockTable + dwFreeBlock; } else { nError = ERROR_DISK_FULL; } } // Calculate the index to the block table hf->dwBlockIndex = (DWORD)(hf->pBlock - ha->pBlockTable); } // Create key for file encryption if(nError == ERROR_SUCCESS && (dwFlags & MPQ_FILE_ENCRYPTED)) { szArchivedName = GetPlainMpqFileName(szArchivedName); hf->dwFileKey = DecryptFileKey(szArchivedName); if(dwFlags & MPQ_FILE_FIX_KEY) hf->dwFileKey = (hf->dwFileKey + hf->MpqFilePos.LowPart) ^ dwFileSize; } if(nError == ERROR_SUCCESS) { // Initialize the hash entry for the file hf->pHash->dwBlockIndex = hf->dwBlockIndex; hf->pHash->lcLocale = (USHORT)lcLocale; // Initialize the block table entry for the file hf->pBlockEx->wFilePosHigh = (USHORT)hf->MpqFilePos.HighPart; hf->pBlock->dwFilePos = hf->MpqFilePos.LowPart; hf->pBlock->dwFSize = dwFileSize; hf->pBlock->dwCSize = 0; hf->pBlock->dwFlags = dwFlags | MPQ_FILE_EXISTS; // Resolve CRC32 and MD5 entry for the file // Only do it when the MPQ archive has attributes if(ha->pAttributes != NULL) { hf->pFileTime = ha->pAttributes->pFileTime + hf->dwBlockIndex; hf->pCrc32 = ha->pAttributes->pCrc32 + hf->dwBlockIndex; hf->pMd5 = ha->pAttributes->pMd5 + hf->dwBlockIndex; // If the file has been overwritten, there still might be // stale entries in the attributes memset(hf->pFileTime, 0, sizeof(TMPQFileTime)); memset(hf->pMd5, 0, sizeof(TMPQMD5)); hf->pCrc32[0] = 0; // Initialize the file time, CRC32 and MD5 assert(sizeof(hf->hctx) >= sizeof(hash_state)); tommd5_init((hash_state *)hf->hctx); hf->dwCrc32 = crc32(0, Z_NULL, 0); // If the caller gave us a file time, use it. if(pFT != NULL) *hf->pFileTime = *pFT; } // Call the callback, if needed if(AddFileCB != NULL) AddFileCB(pvUserData, 0, hf->pBlock->dwFSize, false); } // If an error occured, remember it if(nError != ERROR_SUCCESS) hf->bErrorOccured = true; *phf = hf; return nError; }
int SFileAddFile_Init( TMPQArchive * ha, const char * szFileName, ULONGLONG FileTime, DWORD dwFileSize, LCID lcLocale, DWORD dwFlags, TMPQFile ** phf) { TFileEntry * pFileEntry = NULL; TMPQFile * hf = NULL; // File structure for newly added file DWORD dwHashIndex = HASH_ENTRY_FREE; int nError = ERROR_SUCCESS; // // Note: This is an internal function so no validity checks are done. // It is the caller's responsibility to make sure that no invalid // flags get to this point // // Sestor CRC is not allowed with single unit files if(dwFlags & MPQ_FILE_SINGLE_UNIT) dwFlags &= ~MPQ_FILE_SECTOR_CRC; // Sector CRC is not allowed if the file is not compressed if(!(dwFlags & MPQ_FILE_COMPRESS_MASK)) dwFlags &= ~MPQ_FILE_SECTOR_CRC; // Fix Key is not allowed if the file is not enrypted if(!(dwFlags & MPQ_FILE_ENCRYPTED)) dwFlags &= ~MPQ_FILE_FIX_KEY; // If the MPQ is of version 3.0 or higher, we ignore file locale. // This is because HET and BET tables have no known support for it if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_3) lcLocale = 0; // Allocate the TMPQFile entry for newly added file hf = CreateWritableHandle(ha, dwFileSize); if(hf == NULL) return false; // Allocate file entry in the MPQ if(nError == ERROR_SUCCESS) { // Check if the file already exists in the archive pFileEntry = GetFileEntryExact(ha, szFileName, lcLocale, &dwHashIndex); if(pFileEntry != NULL) { if(dwFlags & MPQ_FILE_REPLACEEXISTING) InvalidateInternalFiles(ha); else nError = ERROR_ALREADY_EXISTS; } else { // Free all internal files - (listfile), (attributes), (signature) InvalidateInternalFiles(ha); // Attempt to allocate new file entry pFileEntry = AllocateFileEntry(ha, szFileName, lcLocale, &dwHashIndex); if(pFileEntry == NULL) nError = ERROR_DISK_FULL; } // Set the file entry to the file structure hf->pFileEntry = pFileEntry; } // Prepare the pointer to hash table entry if(nError == ERROR_SUCCESS && ha->pHashTable != NULL && dwHashIndex < ha->pHeader->dwHashTableSize) { hf->pHashEntry = ha->pHashTable + dwHashIndex; hf->pHashEntry->lcLocale = (USHORT)lcLocale; } // Prepare the file key if(nError == ERROR_SUCCESS && (dwFlags & MPQ_FILE_ENCRYPTED)) { hf->dwFileKey = DecryptFileKey(szFileName, hf->MpqFilePos, dwFileSize, dwFlags); if(hf->dwFileKey == 0) nError = ERROR_UNKNOWN_FILE_KEY; } // Fill the file entry and TMPQFile structure if(nError == ERROR_SUCCESS) { // At this point, the file name in the file entry must be set assert(pFileEntry->szFileName != NULL); assert(_stricmp(pFileEntry->szFileName, szFileName) == 0); nError = FillWritableHandle(ha, hf, FileTime, dwFileSize, dwFlags); } // Free the file handle if failed if(nError != ERROR_SUCCESS && hf != NULL) FreeFileHandle(hf); // Give the handle to the caller *phf = hf; return nError; }
bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * PtrFile) { TMPQArchive * ha = IsValidMpqHandle(hMpq); TFileEntry * pFileEntry = NULL; TMPQFile * hf = NULL; DWORD dwHashIndex = HASH_ENTRY_FREE; DWORD dwFileIndex = 0; bool bOpenByIndex = false; int nError = ERROR_SUCCESS; // Don't accept NULL pointer to file handle if(szFileName == NULL || *szFileName == 0) nError = ERROR_INVALID_PARAMETER; // When opening a file from MPQ, the handle must be valid if(dwSearchScope != SFILE_OPEN_LOCAL_FILE && ha == NULL) nError = ERROR_INVALID_HANDLE; // When not checking for existence, the pointer to file handle must be valid if(dwSearchScope != SFILE_OPEN_CHECK_EXISTS && PtrFile == NULL) nError = ERROR_INVALID_PARAMETER; // Prepare the file opening if(nError == ERROR_SUCCESS) { switch(dwSearchScope) { case SFILE_OPEN_FROM_MPQ: case SFILE_OPEN_BASE_FILE: case SFILE_OPEN_CHECK_EXISTS: // Check the pseudo-file name if((bOpenByIndex = IsPseudoFileName(szFileName, &dwFileIndex)) == true) { // Get the file entry for the file if(dwFileIndex > ha->dwFileTableSize) break; pFileEntry = ha->pFileTable + dwFileIndex; } else { // If this MPQ has no patches, open the file from this MPQ directly if(ha->haPatch == NULL || dwSearchScope == SFILE_OPEN_BASE_FILE) { pFileEntry = GetFileEntryLocale2(ha, szFileName, lcFileLocale, &dwHashIndex); } // If this MPQ is a patched archive, open the file as patched else { return OpenPatchedFile(hMpq, szFileName, PtrFile); } } break; case SFILE_OPEN_ANY_LOCALE: // This open option is reserved for opening MPQ internal listfile. // No argument validation. Tries to open file with neutral locale first, // then any other available. pFileEntry = GetFileEntryLocale2(ha, szFileName, 0, &dwHashIndex); break; case SFILE_OPEN_LOCAL_FILE: // Open a local file return OpenLocalFile(szFileName, PtrFile); default: // Don't accept any other value nError = ERROR_INVALID_PARAMETER; break; } } // Check whether the file really exists in the MPQ if(nError == ERROR_SUCCESS) { if(pFileEntry == NULL || (pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0) nError = ERROR_FILE_NOT_FOUND; // Ignore unknown loading flags (example: MPQ_2016_v1_WME4_4.w3x) // if(pFileEntry != NULL && pFileEntry->dwFlags & ~MPQ_FILE_VALID_FLAGS) // nError = ERROR_NOT_SUPPORTED; } // Did the caller just wanted to know if the file exists? if(nError == ERROR_SUCCESS && dwSearchScope != SFILE_OPEN_CHECK_EXISTS) { // Allocate file handle hf = CreateFileHandle(ha, pFileEntry); if(hf != NULL) { // Get the hash index for the file if(ha->pHashTable != NULL && dwHashIndex == HASH_ENTRY_FREE) dwHashIndex = FindHashIndex(ha, dwFileIndex); if(dwHashIndex != HASH_ENTRY_FREE) hf->pHashEntry = ha->pHashTable + dwHashIndex; hf->dwHashIndex = dwHashIndex; // If the MPQ has sector CRC enabled, enable if for the file if(ha->dwFlags & MPQ_FLAG_CHECK_SECTOR_CRC) hf->bCheckSectorCRCs = true; // If we know the real file name, copy it to the file entry if(bOpenByIndex == false) { // If there is no file name yet, allocate it AllocateFileName(ha, pFileEntry, szFileName); // If the file is encrypted, we should detect the file key if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) { hf->dwFileKey = DecryptFileKey(szFileName, pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags); } } } else { nError = ERROR_NOT_ENOUGH_MEMORY; } } // Give the file entry if(PtrFile != NULL) PtrFile[0] = hf; // Return error code if(nError != ERROR_SUCCESS) SetLastError(nError); return (nError == ERROR_SUCCESS); }
static int CheckIfAllFilesKnown(TMPQArchive * ha, const char * szListFile, DWORD * pFileKeys) { TMPQHash * pHashTableCopy = NULL; // Copy of the hash table TMPQHash * pHash; TMPQHash * pHashEnd = NULL; // End of the hash table DWORD dwFileCount = 0; int nError = ERROR_SUCCESS; // First of all, create a copy of hash table if(nError == ERROR_SUCCESS) { if((pHashTableCopy = CopyHashTable(ha)) == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; pHashEnd = pHashTableCopy + ha->pHeader->dwHashTableSize; // Notify the user if(CompactCB != NULL) CompactCB(pvUserData, CCB_CHECKING_FILES, &CompactBytesProcessed, &CompactTotalBytes); } // Now check all the files from the listfile if(nError == ERROR_SUCCESS) { SFILE_FIND_DATA wf; HANDLE hFind = SFileFindFirstFile((HANDLE)ha, "*", &wf, szListFile); bool bResult = true; // Do while something has been found while(hFind != NULL && bResult) { TMPQHash * pHash = ha->pHashTable + wf.dwHashIndex; // Find the entry in the hash table copy pHash = pHashTableCopy + (pHash - ha->pHashTable); if(pHash->dwBlockIndex != HASH_ENTRY_FREE) { TMPQBlock * pBlock = ha->pBlockTable + pHash->dwBlockIndex; DWORD dwFileKey = 0; // Resolve the file key. Use plain file name for it if(pBlock->dwFlags & MPQ_FILE_ENCRYPTED) { const char * szPlainName = GetPlainMpqFileName(wf.cFileName); dwFileKey = DecryptFileKey(szPlainName); if(pBlock->dwFlags & MPQ_FILE_FIX_KEY) dwFileKey = (dwFileKey + pBlock->dwFilePos) ^ pBlock->dwFSize; } // Give the key to the caller pFileKeys[pHash->dwBlockIndex] = dwFileKey; pHash->dwName1 = 0xFFFFFFFF; pHash->dwName2 = 0xFFFFFFFF; pHash->lcLocale = 0xFFFF; pHash->wPlatform = 0xFFFF; pHash->dwBlockIndex = HASH_ENTRY_FREE; } // Find the next file in the archive bResult = SFileFindNextFile(hFind, &wf); } if(hFind != NULL) SFileFindClose(hFind); } // When the filelist checking is complete, parse the hash table copy and find the if(nError == ERROR_SUCCESS) { // Notify the user about checking hash table dwFileCount = 0; if(CompactCB != NULL) CompactCB(pvUserData, CCB_CHECKING_HASH_TABLE, &CompactBytesProcessed, &CompactTotalBytes); for(pHash = pHashTableCopy; pHash < pHashEnd; pHash++) { // If there is an unresolved entry, try to detect its key. If it fails, // we cannot complete the work if(pHash->dwBlockIndex < ha->pHeader->dwBlockTableSize) { HANDLE hFile = NULL; DWORD dwFlags = 0; DWORD dwFileKey = 0; if(SFileOpenFileEx((HANDLE)ha, (char *)(DWORD_PTR)pHash->dwBlockIndex, SFILE_OPEN_BY_INDEX, &hFile)) { TMPQFile * hf = (TMPQFile *)hFile; dwFlags = hf->pBlock->dwFlags; dwFileKey = hf->dwFileKey; SFileCloseFile(hFile); } // If the file is encrypted, we have to check // If we can apply the file decryption key if((dwFlags & MPQ_FILE_ENCRYPTED) && dwFileKey == 0) { nError = ERROR_CAN_NOT_COMPLETE; break; } // Give the key to the caller pFileKeys[pHash->dwBlockIndex] = dwFileKey; } } } // Delete the copy of hash table if(pHashTableCopy != NULL) FREEMEM(pHashTableCopy); return nError; }
static int CheckIfAllKeysKnown(TMPQArchive * ha, const char * szListFile, uint32_t * pFileKeys) { TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; TFileEntry * pFileEntry; uint32_t dwBlockIndex = 0; int nError = ERROR_SUCCESS; /* Add the listfile to the MPQ */ if(szListFile != NULL) { /* Notify the user */ if(ha->pfnCompactCB != NULL) ha->pfnCompactCB(ha->pvCompactUserData, CCB_CHECKING_FILES, ha->CompactBytesProcessed, ha->CompactTotalBytes); nError = SFileAddListFile((void *)ha, szListFile); } /* Verify the file table */ if(nError == ERROR_SUCCESS) { for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++, dwBlockIndex++) { /* If the file exists and it's encrypted */ if(pFileEntry->dwFlags & MPQ_FILE_EXISTS) { /* If we know the name, we decrypt the file key from the file name */ if(pFileEntry->szFileName != NULL && !IsPseudoFileName(pFileEntry->szFileName, NULL)) { /* Give the key to the caller */ pFileKeys[dwBlockIndex] = DecryptFileKey(pFileEntry->szFileName, pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags); continue; } /* * If the file has a nonzero size, we can try to read few bytes of data * and force to detect the decryption key that way if(pFileEntry->dwFileSize > 0x10) { TMPQFile * hf = NULL; uint32_t dwBytesRead = 0; uint32_t FileData[4]; * Create file handle where we load the sector offset table hf = CreateFileHandle(ha, pFileEntry); if(hf != NULL) { * Call one dummy load of the first 4 bytes. * This enforces loading all buffers and also detecting of the decryption key SFileReadFile((void *)hf, FileData, sizeof(FileData), &dwBytesRead, NULL); pFileKeys[dwBlockIndex] = hf->dwFileKey; FreeFileHandle(hf); } * If we succeeded in reading 16 bytes from the file, * we also know the encryption key if(dwBytesRead == sizeof(FileData)) continue; } */ /* We don't know the encryption key of this file, */ /* thus we cannot compact the file */ nError = ERROR_UNKNOWN_FILE_NAMES; break; } } } return nError; }