Ejemplo n.º 1
0
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;
}
Ejemplo n.º 2
0
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;
}
Ejemplo n.º 3
0
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;
}
Ejemplo n.º 4
0
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);
}
Ejemplo n.º 5
0
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);
}
Ejemplo n.º 6
0
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;
}
Ejemplo n.º 7
0
// 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;
}
Ejemplo n.º 8
0
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;
}