Esempio n. 1
0
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;
}