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;
}
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;
}