bool WINAPI SFileHasFile(HANDLE hMpq, const char * szFileName) { TMPQArchive * ha = (TMPQArchive *)hMpq; TFileEntry * pFileEntry; DWORD dwFlagsToCheck = MPQ_FILE_EXISTS; DWORD dwFileIndex = 0; char szPatchFileName[MAX_PATH]; bool bIsPseudoName; int nError = ERROR_SUCCESS; if(!IsValidMpqHandle(ha)) nError = ERROR_INVALID_HANDLE; if(szFileName == NULL || *szFileName == 0) nError = ERROR_INVALID_PARAMETER; // Prepare the file opening if(nError == ERROR_SUCCESS) { // Different processing for pseudo-names bIsPseudoName = IsPseudoFileName(szFileName, &dwFileIndex); // Walk through the MPQ and all patches while(ha != NULL) { // Verify presence of the file pFileEntry = (bIsPseudoName == false) ? GetFileEntryLocale(ha, szFileName, lcFileLocale) : GetFileEntryByIndex(ha, dwFileIndex); // Verify the file flags if(pFileEntry != NULL && (pFileEntry->dwFlags & dwFlagsToCheck) == MPQ_FILE_EXISTS) return true; // If this is patched archive, go to the patch dwFlagsToCheck = MPQ_FILE_EXISTS | MPQ_FILE_PATCH_FILE; ha = ha->haPatch; // Prepare the patched file name if(ha != NULL) { strcpy(szPatchFileName, ha->szPatchPrefix); strcat(szPatchFileName, szFileName); szFileName = szPatchFileName; } } // Not found, sorry nError = ERROR_FILE_NOT_FOUND; } // Cleanup SetLastError(nError); return false; }
int WINAPI SFileEnumLocales( HANDLE hMpq, const char * szFileName, LCID * PtrLocales, LPDWORD PtrMaxLocales, DWORD dwSearchScope) { TMPQArchive * ha = (TMPQArchive *)hMpq; TMPQHash * pFirstHash; TMPQHash * pHash; DWORD dwFileIndex = 0; DWORD dwMaxLocales; DWORD dwLocales = 0; // Test the parameters if(!IsValidMpqHandle(hMpq)) return ERROR_INVALID_HANDLE; if(szFileName == NULL || *szFileName == 0) return ERROR_INVALID_PARAMETER; if(ha->pHashTable == NULL) return ERROR_NOT_SUPPORTED; if(PtrMaxLocales == NULL) return ERROR_INVALID_PARAMETER; if(IsPseudoFileName(szFileName, &dwFileIndex)) return ERROR_INVALID_PARAMETER; // Keep compiler happy dwMaxLocales = PtrMaxLocales[0]; dwSearchScope = dwSearchScope; // Parse all files with that name pFirstHash = pHash = GetFirstHashEntry(ha, szFileName); while(pHash != NULL) { // Put the locales to the buffer if(PtrLocales != NULL && dwLocales < dwMaxLocales) *PtrLocales++ = pHash->lcLocale; dwLocales++; // Get the next locale pHash = GetNextHashEntry(ha, pFirstHash, pHash); } // Give the caller the number of locales and return PtrMaxLocales[0] = dwLocales; return (dwLocales <= dwMaxLocales) ? ERROR_SUCCESS : ERROR_INSUFFICIENT_BUFFER; }
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); }
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 WINAPI SFileEnumLocales( HANDLE hMpq, const char * szFileName, LCID * plcLocales, LPDWORD pdwMaxLocales, DWORD dwSearchScope) { TMPQArchive * ha = (TMPQArchive *)hMpq; TFileEntry * pFileEntry; TMPQHash * pFirstHash; TMPQHash * pHash; DWORD dwFileIndex = 0; DWORD dwLocales = 0; // Test the parameters if(!IsValidMpqHandle(ha)) return ERROR_INVALID_HANDLE; if(szFileName == NULL || *szFileName == 0) return ERROR_INVALID_PARAMETER; if(pdwMaxLocales == NULL) return ERROR_INVALID_PARAMETER; // Keep compiler happy dwSearchScope = dwSearchScope; // Parse hash table entries for all locales if(!IsPseudoFileName(szFileName, &dwFileIndex)) { // Calculate the number of locales pFirstHash = pHash = GetFirstHashEntry(ha, szFileName); while(pHash != NULL) { dwLocales++; pHash = GetNextHashEntry(ha, pFirstHash, pHash); } // Test if there is enough space to copy the locales if(*pdwMaxLocales < dwLocales) { *pdwMaxLocales = dwLocales; return ERROR_INSUFFICIENT_BUFFER; } // Enum the locales pFirstHash = pHash = GetFirstHashEntry(ha, szFileName); while(pHash != NULL) { *plcLocales++ = pHash->lcLocale; pHash = GetNextHashEntry(ha, pFirstHash, pHash); } } else { // There must be space for 1 locale if(*pdwMaxLocales < 1) { *pdwMaxLocales = 1; return ERROR_INSUFFICIENT_BUFFER; } // For nameless access, always return 1 locale pFileEntry = GetFileEntryByIndex(ha, dwFileIndex); pHash = ha->pHashTable + pFileEntry->dwHashIndex; *plcLocales = pHash->lcLocale; dwLocales = 1; } // Give the caller the total number of found locales *pdwMaxLocales = dwLocales; return ERROR_SUCCESS; }
// Saves the whole listfile into the MPQ. int SListFileSaveToMpq(TMPQArchive * ha) { TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; TFileEntry * pFileEntry; TMPQFile * hf = NULL; char * szPrevItem; char ** SortTable = NULL; DWORD dwFileSize = 0; size_t nFileNodes = 0; size_t i; int nError = ERROR_SUCCESS; // Allocate the table for sorting listfile SortTable = STORM_ALLOC(char*, ha->dwFileTableSize); if(SortTable == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Construct the sort table // Note: in MPQs with multiple locale versions of the same file, // this code causes adding multiple listfile entries. // Since those MPQs were last time used in Starcraft, // we leave it as it is. for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) { // Only take existing items if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && pFileEntry->szFileName != NULL) { // Ignore pseudo-names if(!IsPseudoFileName(pFileEntry->szFileName, NULL) && !IsInternalMpqFileName(pFileEntry->szFileName)) { SortTable[nFileNodes++] = pFileEntry->szFileName; } } } // Sort the table qsort(SortTable, nFileNodes, sizeof(char *), CompareFileNodes); // Now parse the table of file names again - remove duplicates // and count file size. if(nFileNodes != 0) { // Count the 0-th item dwFileSize += (DWORD)strlen(SortTable[0]) + 2; szPrevItem = SortTable[0]; // Count all next items for(i = 1; i < nFileNodes; i++) { // If the item is the same like the last one, skip it if(_stricmp(SortTable[i], szPrevItem)) { dwFileSize += (DWORD)strlen(SortTable[i]) + 2; szPrevItem = SortTable[i]; } } // Determine the flags for (listfile) if(ha->dwFileFlags1 == 0) ha->dwFileFlags1 = GetDefaultSpecialFileFlags(ha, dwFileSize); // Create the listfile in the MPQ nError = SFileAddFile_Init(ha, LISTFILE_NAME, 0, dwFileSize, LANG_NEUTRAL, ha->dwFileFlags1 | MPQ_FILE_REPLACEEXISTING, &hf); // Add all file names if(nError == ERROR_SUCCESS) { // Each name is followed by newline ("\x0D\x0A") szPrevItem = SortTable[0]; nError = WriteListFileLine(hf, SortTable[0]); // Count all next items for(i = 1; i < nFileNodes; i++) { // If the item is the same like the last one, skip it if(_stricmp(SortTable[i], szPrevItem)) { WriteListFileLine(hf, SortTable[i]); szPrevItem = SortTable[i]; } } } } else { // Create the listfile in the MPQ dwFileSize = (DWORD)strlen(LISTFILE_NAME) + 2; nError = SFileAddFile_Init(ha, LISTFILE_NAME, 0, dwFileSize, LANG_NEUTRAL, MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS | MPQ_FILE_REPLACEEXISTING, &hf); // Just add "(listfile)" there if(nError == ERROR_SUCCESS) { WriteListFileLine(hf, LISTFILE_NAME); } } // Finalize the file in the MPQ if(hf != NULL) { SFileAddFile_Finish(hf); } // Free buffers if(nError == ERROR_SUCCESS) ha->dwFlags &= ~MPQ_FLAG_INV_LISTFILE; if(SortTable != NULL) STORM_FREE(SortTable); 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 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; }