static int RecryptFileData( TMPQArchive * ha, DWORD dwSaveBlockIndex, const char * szFileName, const char * szNewFileName) { LARGE_INTEGER BlockFilePos; LARGE_INTEGER MpqFilePos; TMPQBlockEx * pBlockEx = ha->pExtBlockTable + dwSaveBlockIndex; TMPQBlock * pBlock = ha->pBlockTable + dwSaveBlockIndex; const char * szPlainName; LPDWORD pdwBlockPos1 = NULL; LPDWORD pdwBlockPos2 = NULL; LPBYTE pbFileBlock = NULL; DWORD dwTransferred; DWORD dwOldSeed; DWORD dwNewSeed; DWORD dwToRead; int nBlocks; int nError = ERROR_SUCCESS; // The file must be encrypted assert(pBlock->dwFlags & MPQ_FILE_ENCRYPTED); // File decryption seed is calculated from the plain name szPlainName = strrchr(szFileName, '\\'); if(szPlainName != NULL) szFileName = szPlainName + 1; szPlainName = strrchr(szNewFileName, '\\'); if(szPlainName != NULL) szNewFileName = szPlainName + 1; // Calculate both file seeds dwOldSeed = DecryptFileSeed(szFileName); dwNewSeed = DecryptFileSeed(szNewFileName); if(pBlock->dwFlags & MPQ_FILE_FIXSEED) { dwOldSeed = (dwOldSeed + pBlock->dwFilePos) ^ pBlock->dwFSize; dwNewSeed = (dwNewSeed + pBlock->dwFilePos) ^ pBlock->dwFSize; } // Incase the seeds are equal, don't recrypt the file if(dwNewSeed == dwOldSeed) return ERROR_SUCCESS; // Calculate the file position of the archived file MpqFilePos.LowPart = pBlock->dwFilePos; MpqFilePos.HighPart = pBlockEx->wFilePosHigh; MpqFilePos.QuadPart += ha->MpqPos.QuadPart; // Calculate the number of file blocks nBlocks = pBlock->dwFSize / ha->dwBlockSize; if(pBlock->dwFSize % ha->dwBlockSize) nBlocks++; // If the file is stored as single unit, we recrypt one block only if(pBlock->dwFlags & MPQ_FILE_SINGLE_UNIT) { // Allocate the block pbFileBlock = ALLOCMEM(BYTE, pBlock->dwCSize); if(pbFileBlock == NULL) return ERROR_NOT_ENOUGH_MEMORY; SetFilePointer(ha->hFile, MpqFilePos.LowPart, &MpqFilePos.HighPart, FILE_BEGIN); ReadFile(ha->hFile, pbFileBlock, pBlock->dwCSize, &dwTransferred, NULL); if(dwTransferred == pBlock->dwCSize) nError = ERROR_FILE_CORRUPT; if(nError == ERROR_SUCCESS) { // Recrypt the block DecryptMPQBlock((DWORD *)pbFileBlock, pBlock->dwCSize, dwOldSeed); EncryptMPQBlock((DWORD *)pbFileBlock, pBlock->dwCSize, dwNewSeed); // Write it back SetFilePointer(ha->hFile, MpqFilePos.LowPart, &MpqFilePos.HighPart, FILE_BEGIN); WriteFile(ha->hFile, pbFileBlock, pBlock->dwCSize, &dwTransferred, NULL); if(dwTransferred != pBlock->dwCSize) nError = ERROR_WRITE_FAULT; } FREEMEM(pbFileBlock); return nError; } // If the file is compressed, we have to re-crypt block table first, // then all file blocks if(pBlock->dwFlags & MPQ_FILE_COMPRESSED) { // Allocate buffer for both blocks pdwBlockPos1 = ALLOCMEM(DWORD, nBlocks + 2); pdwBlockPos2 = ALLOCMEM(DWORD, nBlocks + 2); if(pdwBlockPos1 == NULL || pdwBlockPos2 == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Calculate number of bytes to be read dwToRead = (nBlocks + 1) * sizeof(DWORD); if(pBlock->dwFlags & MPQ_FILE_HAS_EXTRA) dwToRead += sizeof(DWORD); // Read the block positions SetFilePointer(ha->hFile, MpqFilePos.LowPart, &MpqFilePos.HighPart, FILE_BEGIN); ReadFile(ha->hFile, pdwBlockPos1, dwToRead, &dwTransferred, NULL); if(dwTransferred != dwToRead) nError = ERROR_FILE_CORRUPT; // Recrypt the block table if(nError == ERROR_SUCCESS) { BSWAP_ARRAY32_UNSIGNED(pdwBlockPos1, dwToRead / sizeof(DWORD)); DecryptMPQBlock(pdwBlockPos1, dwToRead, dwOldSeed - 1); if(pdwBlockPos1[0] != dwToRead) nError = ERROR_FILE_CORRUPT; memcpy(pdwBlockPos2, pdwBlockPos1, dwToRead); EncryptMPQBlock(pdwBlockPos2, dwToRead, dwNewSeed - 1); BSWAP_ARRAY32_UNSIGNED(pdwBlockPos2, dwToRead / sizeof(DWORD)); } // Write the recrypted block table back if(nError == ERROR_SUCCESS) { SetFilePointer(ha->hFile, MpqFilePos.LowPart, &MpqFilePos.HighPart, FILE_BEGIN); WriteFile(ha->hFile, pdwBlockPos2, dwToRead, &dwTransferred, NULL); if(dwTransferred != dwToRead) nError = ERROR_WRITE_FAULT; } } // Allocate the transfer buffer if(nError == ERROR_SUCCESS) { pbFileBlock = ALLOCMEM(BYTE, ha->dwBlockSize); if(pbFileBlock == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; } // Now we have to recrypt all file blocks if(nError == ERROR_SUCCESS) { for(int nBlock = 0; nBlock < nBlocks; nBlock++) { // Calc position and length for uncompressed file BlockFilePos.QuadPart = MpqFilePos.QuadPart + (ha->dwBlockSize * nBlock); dwToRead = ha->dwBlockSize; if(nBlock == nBlocks - 1) dwToRead = pBlock->dwFSize - (ha->dwBlockSize * (nBlocks - 1)); // Fix position and length for compressed file if(pBlock->dwFlags & MPQ_FILE_COMPRESS) { BlockFilePos.QuadPart = MpqFilePos.QuadPart + pdwBlockPos1[nBlock]; dwToRead = pdwBlockPos1[nBlock+1] - pdwBlockPos1[nBlock]; } // Read the file block SetFilePointer(ha->hFile, BlockFilePos.LowPart, &BlockFilePos.HighPart, FILE_BEGIN); ReadFile(ha->hFile, pbFileBlock, dwToRead, &dwTransferred, NULL); if(dwTransferred != dwToRead) nError = ERROR_FILE_CORRUPT; // Recrypt the file block BSWAP_ARRAY32_UNSIGNED((DWORD *)pbFileBlock, dwToRead/sizeof(DWORD)); DecryptMPQBlock((DWORD *)pbFileBlock, dwToRead, dwOldSeed + nBlock); EncryptMPQBlock((DWORD *)pbFileBlock, dwToRead, dwNewSeed + nBlock); BSWAP_ARRAY32_UNSIGNED((DWORD *)pbFileBlock, dwToRead/sizeof(DWORD)); // Write the block back SetFilePointer(ha->hFile, BlockFilePos.LowPart, &BlockFilePos.HighPart, FILE_BEGIN); WriteFile(ha->hFile, pbFileBlock, dwToRead, &dwTransferred, NULL); if(dwTransferred != dwToRead) nError = ERROR_WRITE_FAULT; } } // Free buffers and exit if(pbFileBlock != NULL) FREEMEM(pbFileBlock); if(pdwBlockPos2 != NULL) FREEMEM(pdwBlockPos2); if(pdwBlockPos1 != NULL) FREEMEM(pdwBlockPos1); return nError; }
static int CheckIfAllFilesKnown(TMPQArchive * ha, const char * szListFile, DWORD * pFileSeeds) { 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(lpUserData, CCB_CHECKING_FILES, 0, ha->pHeader->dwBlockTableSize); } // Now check all the files from the filelist if(nError == ERROR_SUCCESS) { SFILE_FIND_DATA wf; HANDLE hFind = SFileFindFirstFile((HANDLE)ha, "*", &wf, szListFile); BOOL bResult = TRUE; // Do while some files have been found while(hFind != NULL && bResult) { TMPQHash * pHash = GetHashEntry(ha, wf.cFileName); // If the hash table entry has been found, find it's position // in the hash table copy if(pHash != NULL) { pHash = pHashTableCopy + (pHash - ha->pHashTable); if(pHash->dwName1 != (DWORD)-1 && pHash->dwName2 != (DWORD)-1) { TMPQBlock * pBlock = ha->pBlockTable + pHash->dwBlockIndex; DWORD dwSeed = 0; // Resolve the file seed. Use plain file name for it if(pBlock->dwFlags & MPQ_FILE_ENCRYPTED) { char * szFileName = strrchr(wf.cFileName, '\\'); if(szFileName == NULL) szFileName = wf.cFileName; else szFileName++; dwSeed = DecryptFileSeed(szFileName); if(pBlock->dwFlags & MPQ_FILE_FIXSEED) dwSeed = (dwSeed + pBlock->dwFilePos) ^ pBlock->dwFSize; } pFileSeeds[pHash->dwBlockIndex] = dwSeed; pHash->dwName1 = 0xFFFFFFFF; pHash->dwName2 = 0xFFFFFFFF; pHash->lcLocale = 0xFFFF; pHash->wPlatform = 0xFFFF; pHash->dwBlockIndex = 0xFFFFFFFF; } } // Notify the user if(CompactCB != NULL) CompactCB(lpUserData, CCB_CHECKING_FILES, ++dwFileCount, ha->pHeader->dwBlockTableSize); // 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(lpUserData, CCB_CHECKING_HASH_TABLE, dwFileCount, ha->pHeader->dwBlockTableSize); for(pHash = pHashTableCopy; pHash < pHashEnd; pHash++) { // If there is an unresolved entry, try to detect its seed. If it fails, // we cannot complete the work if(pHash->dwName1 != (DWORD)-1 && pHash->dwName2 != (DWORD)-1) { HANDLE hFile = NULL; DWORD dwFlags = 0; DWORD dwSeed = 0; if(SFileOpenFileEx((HANDLE)ha, (char *)(DWORD_PTR)pHash->dwBlockIndex, SFILE_OPEN_BY_INDEX, &hFile)) { TMPQFile * hf = (TMPQFile *)hFile; dwFlags = hf->pBlock->dwFlags; dwSeed = hf->dwSeed1; SFileCloseFile(hFile); } // If the file is encrypted, we have to check // If we can apply the file decryption seed if(dwFlags & MPQ_FILE_ENCRYPTED && dwSeed == 0) { nError = ERROR_CAN_NOT_COMPLETE; break; } // Remember the seed pFileSeeds[pHash->dwBlockIndex] = dwSeed; // Notify the user if(CompactCB != NULL) CompactCB(lpUserData, CCB_CHECKING_HASH_TABLE, ++dwFileCount, ha->pHeader->dwBlockTableSize); } } } // Delete the copy of hash table if(pHashTableCopy != NULL) FREEMEM(pHashTableCopy); return nError; }