Exemplo n.º 1
0
bool WINAPI SFileGetFileInfo(
    HANDLE hMpqOrFile,
    SFileInfoClass InfoClass,
    void * pvFileInfo,
    DWORD cbFileInfo,
    LPDWORD pcbLengthNeeded)
{
    MPQ_SIGNATURE_INFO SignatureInfo;
    TMPQArchive * ha = NULL;
    TFileEntry * pFileEntry = NULL;
    ULONGLONG Int64Value = 0;
    ULONGLONG ByteOffset = 0;
    TMPQHash * pHash;
    TMPQFile * hf = NULL;
    void * pvSrcFileInfo = NULL;
    DWORD cbSrcFileInfo = 0;
    DWORD dwInt32Value = 0;
    int nInfoType = SFILE_INFO_TYPE_INVALID_HANDLE;
    int nError = ERROR_SUCCESS;

    switch(InfoClass)
    {
        case SFileMpqFileName:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                pvSrcFileInfo = (void *)FileStream_GetFileName(ha->pStream);
                cbSrcFileInfo = (DWORD)(_tcslen((TCHAR *)pvSrcFileInfo) + 1) * sizeof(TCHAR);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqStreamBitmap:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
                return FileStream_GetBitmap(ha->pStream, pvFileInfo, cbFileInfo, pcbLengthNeeded);
            break;

        case SFileMpqUserDataOffset:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                nInfoType = SFILE_INFO_TYPE_NOT_FOUND;
                if(ha->pUserData != NULL)
                {
                    pvSrcFileInfo = &ha->UserDataPos;
                    cbSrcFileInfo = sizeof(ULONGLONG);
                    nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
                }
            }
            break;

        case SFileMpqUserDataHeader:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                nInfoType = SFILE_INFO_TYPE_NOT_FOUND;
                if(ha->pUserData != NULL)
                {
                    ByteOffset = ha->UserDataPos;
                    cbSrcFileInfo = sizeof(TMPQUserData);
                    nInfoType = SFILE_INFO_TYPE_READ_FROM_FILE;
                }
            }
            break;

        case SFileMpqUserData:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                nInfoType = SFILE_INFO_TYPE_NOT_FOUND;
                if(ha->pUserData != NULL)
                {
                    ByteOffset = ha->UserDataPos + sizeof(TMPQUserData);
                    cbSrcFileInfo = ha->pUserData->dwHeaderOffs - sizeof(TMPQUserData);
                    nInfoType = SFILE_INFO_TYPE_READ_FROM_FILE;
                }
            }
            break;

        case SFileMpqHeaderOffset:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                pvSrcFileInfo = &ha->MpqPos;
                cbSrcFileInfo = sizeof(ULONGLONG);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqHeaderSize:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                pvSrcFileInfo = &ha->pHeader->dwHeaderSize;
                cbSrcFileInfo = sizeof(DWORD);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqHeader:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                ByteOffset = ha->MpqPos;
                cbSrcFileInfo = ha->pHeader->dwHeaderSize;
                nInfoType = SFILE_INFO_TYPE_READ_FROM_FILE;
            }
            break;

        case SFileMpqHetTableOffset:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                pvSrcFileInfo = &ha->pHeader->HetTablePos64;
                cbSrcFileInfo = sizeof(ULONGLONG);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqHetTableSize:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                pvSrcFileInfo = &ha->pHeader->HetTableSize64;
                cbSrcFileInfo = sizeof(ULONGLONG);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqHetHeader:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                nInfoType = SFILE_INFO_TYPE_NOT_FOUND;
                pvSrcFileInfo = LoadExtTable(ha, ha->pHeader->HetTablePos64, (size_t)ha->pHeader->HetTableSize64, HET_TABLE_SIGNATURE, MPQ_KEY_HASH_TABLE);
                if(pvSrcFileInfo != NULL)
                {
                    cbSrcFileInfo = sizeof(TMPQHetHeader);
                    nInfoType = SFILE_INFO_TYPE_ALLOCATED;
                }
            }
            break;

        case SFileMpqHetTable:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                nInfoType = SFILE_INFO_TYPE_NOT_FOUND;
                pvSrcFileInfo = LoadHetTable(ha);
                if(pvSrcFileInfo != NULL)
                {
                    cbSrcFileInfo = sizeof(void *);
                    nInfoType = SFILE_INFO_TYPE_TABLE_POINTER;
                }
            }
            break;

        case SFileMpqBetTableOffset:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                pvSrcFileInfo = &ha->pHeader->BetTablePos64;
                cbSrcFileInfo = sizeof(ULONGLONG);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqBetTableSize:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                pvSrcFileInfo = &ha->pHeader->BetTableSize64;
                cbSrcFileInfo = sizeof(ULONGLONG);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqBetHeader:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                nInfoType = SFILE_INFO_TYPE_NOT_FOUND;
                pvSrcFileInfo = LoadExtTable(ha, ha->pHeader->BetTablePos64, (size_t)ha->pHeader->BetTableSize64, BET_TABLE_SIGNATURE, MPQ_KEY_BLOCK_TABLE);
                if(pvSrcFileInfo != NULL)
                {
                    // It is allowed for the caller to only require BET header.
                    cbSrcFileInfo = sizeof(TMPQBetHeader) + ((TMPQBetHeader *)pvSrcFileInfo)->dwFlagCount * sizeof(DWORD);
                    if(cbFileInfo == sizeof(TMPQBetHeader))
                        cbSrcFileInfo = sizeof(TMPQBetHeader);
                    nInfoType = SFILE_INFO_TYPE_ALLOCATED;
                }
            }
            break;

        case SFileMpqBetTable:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                nInfoType = SFILE_INFO_TYPE_NOT_FOUND;
                pvSrcFileInfo = LoadBetTable(ha);
                if(pvSrcFileInfo != NULL)
                {
                    cbSrcFileInfo = sizeof(void *);
                    nInfoType = SFILE_INFO_TYPE_TABLE_POINTER;
                }
            }
            break;

        case SFileMpqHashTableOffset:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                Int64Value = MAKE_OFFSET64(ha->pHeader->wHashTablePosHi, ha->pHeader->dwHashTablePos);
                pvSrcFileInfo = &Int64Value;
                cbSrcFileInfo = sizeof(ULONGLONG);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqHashTableSize64:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                pvSrcFileInfo = &ha->pHeader->HashTableSize64;
                cbSrcFileInfo = sizeof(ULONGLONG);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqHashTableSize:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                pvSrcFileInfo = &ha->pHeader->dwHashTableSize;
                cbSrcFileInfo = sizeof(DWORD);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqHashTable:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL && ha->pHashTable != NULL)
            {
                pvSrcFileInfo = ha->pHashTable;
                cbSrcFileInfo = ha->dwHashTableSize * sizeof(TMPQHash);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqBlockTableOffset:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                Int64Value = MAKE_OFFSET64(ha->pHeader->wBlockTablePosHi, ha->pHeader->dwBlockTablePos);
                pvSrcFileInfo = &Int64Value;
                cbSrcFileInfo = sizeof(ULONGLONG);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqBlockTableSize64:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                pvSrcFileInfo = &ha->pHeader->BlockTableSize64;
                cbSrcFileInfo = sizeof(ULONGLONG);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqBlockTableSize:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                pvSrcFileInfo = &ha->pHeader->dwBlockTableSize;
                cbSrcFileInfo = sizeof(DWORD);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqBlockTable:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                nInfoType = SFILE_INFO_TYPE_NOT_FOUND;
                if(MAKE_OFFSET64(ha->pHeader->wBlockTablePosHi, ha->pHeader->dwBlockTablePos) != 0)
                {
                    cbSrcFileInfo = ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock);
                    if(cbFileInfo >= cbSrcFileInfo)
                        pvSrcFileInfo = LoadBlockTable(ha, true);
                    nInfoType = SFILE_INFO_TYPE_ALLOCATED;
                }
            }
            break;

        case SFileMpqHiBlockTableOffset:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                pvSrcFileInfo = &ha->pHeader->HiBlockTablePos64;
                cbSrcFileInfo = sizeof(ULONGLONG);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqHiBlockTableSize64:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                pvSrcFileInfo = &ha->pHeader->HiBlockTableSize64;
                cbSrcFileInfo = sizeof(ULONGLONG);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqHiBlockTable:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                nInfoType = SFILE_INFO_TYPE_NOT_FOUND;
                if(ha->pHeader->HiBlockTablePos64 && ha->pHeader->HiBlockTableSize64)
                {
                    assert(false);
                }
            }
            break;

        case SFileMpqSignatures:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL && QueryMpqSignatureInfo(ha, &SignatureInfo))
            {
                pvSrcFileInfo = &SignatureInfo.SignatureTypes;
                cbSrcFileInfo = sizeof(DWORD);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqStrongSignatureOffset:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                nInfoType = SFILE_INFO_TYPE_NOT_FOUND;
                if(QueryMpqSignatureInfo(ha, &SignatureInfo) && (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG))
                {
                    pvSrcFileInfo = &SignatureInfo.EndMpqData;
                    cbSrcFileInfo = sizeof(ULONGLONG);
                    nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
                }
            }
            break;

        case SFileMpqStrongSignatureSize:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                nInfoType = SFILE_INFO_TYPE_NOT_FOUND;
                if(QueryMpqSignatureInfo(ha, &SignatureInfo) && (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG))
                {
                    dwInt32Value = MPQ_STRONG_SIGNATURE_SIZE + 4;
                    pvSrcFileInfo = &dwInt32Value;
                    cbSrcFileInfo = sizeof(DWORD);
                    nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
                }
            }
            break;

        case SFileMpqStrongSignature:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                nInfoType = SFILE_INFO_TYPE_NOT_FOUND;
                if(QueryMpqSignatureInfo(ha, &SignatureInfo) && (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG))
                {
                    pvSrcFileInfo = SignatureInfo.Signature;
                    cbSrcFileInfo = MPQ_STRONG_SIGNATURE_SIZE + 4;
                    nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
                }
            }
            break;

        case SFileMpqArchiveSize64:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                pvSrcFileInfo = &ha->pHeader->ArchiveSize64;
                cbSrcFileInfo = sizeof(DWORD);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqArchiveSize:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                pvSrcFileInfo = &ha->pHeader->dwArchiveSize;
                cbSrcFileInfo = sizeof(DWORD);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqMaxFileCount:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                pvSrcFileInfo = &ha->dwMaxFileCount;
                cbSrcFileInfo = sizeof(DWORD);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqFileTableSize:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                pvSrcFileInfo = &ha->dwFileTableSize;
                cbSrcFileInfo = sizeof(DWORD);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqSectorSize:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                pvSrcFileInfo = &ha->dwSectorSize;
                cbSrcFileInfo = sizeof(DWORD);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqNumberOfFiles:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                pvSrcFileInfo = &dwInt32Value;
                cbSrcFileInfo = sizeof(DWORD);
                dwInt32Value = GetMpqFileCount(ha);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqRawChunkSize:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                nInfoType = SFILE_INFO_TYPE_NOT_FOUND;
                if(ha->pHeader->dwRawChunkSize != 0)
                {
                    pvSrcFileInfo = &ha->pHeader->dwRawChunkSize;
                    cbSrcFileInfo = sizeof(DWORD);
                    nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
                }
            }
            break;

        case SFileMpqStreamFlags:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                FileStream_GetFlags(ha->pStream, &dwInt32Value);
                pvSrcFileInfo = &dwInt32Value;
                cbSrcFileInfo = sizeof(DWORD);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileMpqFlags:
            ha = IsValidMpqHandle(hMpqOrFile);
            if(ha != NULL)
            {
                dwInt32Value  = ha->dwFlags;
                pvSrcFileInfo = &dwInt32Value;
                cbSrcFileInfo = sizeof(DWORD);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileInfoPatchChain:
            hf = IsValidFileHandle(hMpqOrFile);
            if(hf != NULL)
                return GetFilePatchChain(hf, pvFileInfo, cbFileInfo, pcbLengthNeeded);
            break;

        case SFileInfoFileEntry:
            hf = IsValidFileHandle(hMpqOrFile);
            if(hf != NULL && hf->pFileEntry != NULL)
            {
                pvSrcFileInfo = pFileEntry = hf->pFileEntry;
                cbSrcFileInfo = sizeof(TFileEntry);
                if(pFileEntry->szFileName != NULL)
                    cbSrcFileInfo += (DWORD)strlen(pFileEntry->szFileName) + 1;
                nInfoType = SFILE_INFO_TYPE_FILE_ENTRY;
            }
            break;

        case SFileInfoHashEntry:
            hf = IsValidFileHandle(hMpqOrFile);
            if(hf != NULL && hf->ha != NULL && hf->ha->pHashTable != NULL)
            {
                pvSrcFileInfo = hf->ha->pHashTable + hf->pFileEntry->dwHashIndex;
                cbSrcFileInfo = sizeof(TMPQHash);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileInfoHashIndex:
            hf = IsValidFileHandle(hMpqOrFile);
            if(hf != NULL && hf->pFileEntry != NULL)
            {
                pvSrcFileInfo = &hf->pFileEntry->dwHashIndex;
                cbSrcFileInfo = sizeof(DWORD);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileInfoNameHash1:
            hf = IsValidFileHandle(hMpqOrFile);
            if(hf != NULL && hf->ha != NULL && hf->ha->pHashTable != NULL)
            {
                pHash = hf->ha->pHashTable + hf->pFileEntry->dwHashIndex;
                pvSrcFileInfo = &pHash->dwName1;
                cbSrcFileInfo = sizeof(DWORD);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileInfoNameHash2:
            hf = IsValidFileHandle(hMpqOrFile);
            if(hf != NULL && hf->ha != NULL && hf->ha->pHashTable != NULL)
            {
                pHash = hf->ha->pHashTable + hf->pFileEntry->dwHashIndex;
                pvSrcFileInfo = &pHash->dwName2;
                cbSrcFileInfo = sizeof(DWORD);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileInfoNameHash3:
            hf = IsValidFileHandle(hMpqOrFile);
            if(hf != NULL && hf->pFileEntry != NULL)
            {
                pvSrcFileInfo = &hf->pFileEntry->FileNameHash;
                cbSrcFileInfo = sizeof(ULONGLONG);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileInfoLocale:
            hf = IsValidFileHandle(hMpqOrFile);
            if(hf != NULL && hf->pFileEntry != NULL)
            {
                dwInt32Value = hf->pFileEntry->lcLocale;
                pvSrcFileInfo = &dwInt32Value;
                cbSrcFileInfo = sizeof(DWORD);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileInfoFileIndex:
            hf = IsValidFileHandle(hMpqOrFile);
            if(hf != NULL && hf->ha != NULL && hf->pFileEntry != NULL)
            {
                dwInt32Value = (DWORD)(hf->pFileEntry - hf->ha->pFileTable);
                pvSrcFileInfo = &dwInt32Value;
                cbSrcFileInfo = sizeof(DWORD);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileInfoByteOffset:
            hf = IsValidFileHandle(hMpqOrFile);
            if(hf != NULL && hf->pFileEntry != NULL)
            {
                pvSrcFileInfo = &hf->pFileEntry->ByteOffset;
                cbSrcFileInfo = sizeof(ULONGLONG);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileInfoFileTime:
            hf = IsValidFileHandle(hMpqOrFile);
            if(hf != NULL && hf->pFileEntry != NULL)
            {
                pvSrcFileInfo = &hf->pFileEntry->FileTime;
                cbSrcFileInfo = sizeof(ULONGLONG);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileInfoFileSize:
            hf = IsValidFileHandle(hMpqOrFile);
            if(hf != NULL && hf->pFileEntry != NULL)
            {
                pvSrcFileInfo = &hf->pFileEntry->dwFileSize;
                cbSrcFileInfo = sizeof(DWORD);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileInfoCompressedSize:
            hf = IsValidFileHandle(hMpqOrFile);
            if(hf != NULL && hf->pFileEntry != NULL)
            {
                pvSrcFileInfo = &hf->pFileEntry->dwCmpSize;
                cbSrcFileInfo = sizeof(DWORD);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileInfoFlags:
            hf = IsValidFileHandle(hMpqOrFile);
            if(hf != NULL && hf->pFileEntry != NULL)
            {
                pvSrcFileInfo = &hf->pFileEntry->dwFlags;
                cbSrcFileInfo = sizeof(DWORD);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileInfoEncryptionKey:
            hf = IsValidFileHandle(hMpqOrFile);
            if(hf != NULL)
            {
                pvSrcFileInfo = &hf->dwFileKey;
                cbSrcFileInfo = sizeof(DWORD);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        case SFileInfoEncryptionKeyRaw:
            hf = IsValidFileHandle(hMpqOrFile);
            if(hf != NULL && hf->pFileEntry != NULL)
            {
                dwInt32Value = hf->dwFileKey;
                if(hf->pFileEntry->dwFlags & MPQ_FILE_FIX_KEY)
                    dwInt32Value = (dwInt32Value ^ hf->pFileEntry->dwFileSize) - (DWORD)hf->MpqFilePos;
                pvSrcFileInfo = &dwInt32Value;
                cbSrcFileInfo = sizeof(DWORD);
                nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER;
            }
            break;

        default:    // Invalid info class
            SetLastError(ERROR_INVALID_PARAMETER);
            return false;
    }

    // If we validated the handle and info class, give as much info as possible
    if(nInfoType >= SFILE_INFO_TYPE_DIRECT_POINTER)
    {
        // Give the length needed, if wanted
        if(pcbLengthNeeded != NULL)
            pcbLengthNeeded[0] = cbSrcFileInfo;

        // If the caller entered an output buffer, the output size must also be entered
        if(pvFileInfo != NULL && cbFileInfo != 0)
        {
            // Check if there is enough space in the output buffer
            if(cbSrcFileInfo <= cbFileInfo)
            {
                switch(nInfoType)
                {
                    case SFILE_INFO_TYPE_DIRECT_POINTER:
                    case SFILE_INFO_TYPE_ALLOCATED:
                        assert(pvSrcFileInfo != NULL);
                        memcpy(pvFileInfo, pvSrcFileInfo, cbSrcFileInfo);
                        break;

                    case SFILE_INFO_TYPE_READ_FROM_FILE:
                        if(!FileStream_Read(ha->pStream, &ByteOffset, pvFileInfo, cbSrcFileInfo))
                            nError = GetLastError();
                        break;

                    case SFILE_INFO_TYPE_TABLE_POINTER:
                        assert(pvSrcFileInfo != NULL);
                        *(void **)pvFileInfo = pvSrcFileInfo;
                        pvSrcFileInfo = NULL;
                        break;

                    case SFILE_INFO_TYPE_FILE_ENTRY:
                        assert(pFileEntry != NULL);
                        ConvertFileEntryToSelfRelative((TFileEntry *)pvFileInfo, pFileEntry);
                        break;
                }
            }
            else
            {
                nError = ERROR_INSUFFICIENT_BUFFER;
            }
        }

        // Free the file info if needed
        if(nInfoType == SFILE_INFO_TYPE_ALLOCATED && pvSrcFileInfo != NULL)
            STORM_FREE(pvSrcFileInfo);
        if(nInfoType == SFILE_INFO_TYPE_TABLE_POINTER && pvSrcFileInfo != NULL)
            SFileFreeFileInfo(pvSrcFileInfo, InfoClass);
    }
    else
    {
        // Handle error cases
        if(nInfoType == SFILE_INFO_TYPE_INVALID_HANDLE)
            nError = ERROR_INVALID_HANDLE;
        if(nInfoType == SFILE_INFO_TYPE_NOT_FOUND)
            nError = ERROR_FILE_NOT_FOUND;
    }

    // Set the last error value, if needed
    if(nError != ERROR_SUCCESS)
        SetLastError(nError);
    return (nError == ERROR_SUCCESS);
}
Exemplo n.º 2
0
bool WINAPI SFileOpenArchive(
    const TCHAR * szMpqName,
    DWORD dwPriority,
    DWORD dwFlags,
    HANDLE * phMpq)
{
    TMPQUserData * pUserData;
    TFileStream * pStream = NULL;       // Open file stream
    TMPQArchive * ha = NULL;            // Archive handle
    TFileEntry * pFileEntry;
    ULONGLONG FileSize = 0;             // Size of the file
    LPBYTE pbHeaderBuffer = NULL;       // Buffer for searching MPQ header
    DWORD dwStreamFlags = (dwFlags & STREAM_FLAGS_MASK);
    bool bIsWarcraft3Map = false;
    int nError = ERROR_SUCCESS;   

    // Verify the parameters
    if(szMpqName == NULL || *szMpqName == 0 || phMpq == NULL)
    {
        SetLastError(ERROR_INVALID_PARAMETER);
        return false;
    }

    // One time initialization of MPQ cryptography
    InitializeMpqCryptography();
    dwPriority = dwPriority;

    // If not forcing MPQ v 1.0, also use file bitmap
    dwStreamFlags |= (dwFlags & MPQ_OPEN_FORCE_MPQ_V1) ? 0 : STREAM_FLAG_USE_BITMAP;

    // Open the MPQ archive file
    pStream = FileStream_OpenFile(szMpqName, dwStreamFlags);
    if(pStream == NULL)
        return false;

    // Check the file size. There must be at least 0x20 bytes
    if(nError == ERROR_SUCCESS)
    {
        FileStream_GetSize(pStream, &FileSize);
        if(FileSize < MPQ_HEADER_SIZE_V1)
            nError = ERROR_BAD_FORMAT;
    }

    // Allocate the MPQhandle
    if(nError == ERROR_SUCCESS)
    {
        if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL)
            nError = ERROR_NOT_ENOUGH_MEMORY;
    }

    // Allocate buffer for searching MPQ header
    if(nError == ERROR_SUCCESS)
    {
        pbHeaderBuffer = STORM_ALLOC(BYTE, HEADER_SEARCH_BUFFER_SIZE);
        if(pbHeaderBuffer == NULL)
            nError = ERROR_NOT_ENOUGH_MEMORY;
    }

    // Find the position of MPQ header
    if(nError == ERROR_SUCCESS)
    {
        ULONGLONG SearchOffset = 0;
        ULONGLONG EndOfSearch = FileSize;
        DWORD dwStrmFlags = 0;
        DWORD dwHeaderSize;
        DWORD dwHeaderID;
        bool bSearchComplete = false;

        memset(ha, 0, sizeof(TMPQArchive));
        ha->pfnHashString = HashStringSlash;
        ha->pStream = pStream;
        pStream = NULL;

        // Set the archive read only if the stream is read-only
        FileStream_GetFlags(ha->pStream, &dwStrmFlags);
        ha->dwFlags |= (dwStrmFlags & STREAM_FLAG_READ_ONLY) ? MPQ_FLAG_READ_ONLY : 0;

        // Also remember if we shall check sector CRCs when reading file
        ha->dwFlags |= (dwFlags & MPQ_OPEN_CHECK_SECTOR_CRC) ? MPQ_FLAG_CHECK_SECTOR_CRC : 0;

        // Also remember if this MPQ is a patch
        ha->dwFlags |= (dwFlags & MPQ_OPEN_PATCH) ? MPQ_FLAG_PATCH : 0;
       
        // Limit the header searching to about 130 MB of data
        if(EndOfSearch > 0x08000000)
            EndOfSearch = 0x08000000;

        // Find the offset of MPQ header within the file
        while(bSearchComplete == false && SearchOffset < EndOfSearch)
        {
            // Always read at least 0x1000 bytes for performance.
            // This is what Storm.dll (2002) does.
            DWORD dwBytesAvailable = HEADER_SEARCH_BUFFER_SIZE;
            DWORD dwInBufferOffset = 0;

            // Cut the bytes available, if needed
            if((FileSize - SearchOffset) < HEADER_SEARCH_BUFFER_SIZE)
                dwBytesAvailable = (DWORD)(FileSize - SearchOffset);

            // Read the eventual MPQ header
            if(!FileStream_Read(ha->pStream, &SearchOffset, pbHeaderBuffer, dwBytesAvailable))
            {
                nError = GetLastError();
                break;
            }

            // There are AVI files from Warcraft III with 'MPQ' extension.
            if(SearchOffset == 0)
            {
                if(IsAviFile((DWORD *)pbHeaderBuffer))
                {
                    nError = ERROR_AVI_FILE;
                    break;
                }

                bIsWarcraft3Map = IsWarcraft3Map((DWORD *)pbHeaderBuffer);
            }

            // Search the header buffer
            while(dwInBufferOffset < dwBytesAvailable)
            {
                // Copy the data from the potential header buffer to the MPQ header
                memcpy(ha->HeaderData, pbHeaderBuffer + dwInBufferOffset, sizeof(ha->HeaderData));

                // If there is the MPQ user data, process it
                // Note that Warcraft III does not check for user data, which is abused by many map protectors
                dwHeaderID = BSWAP_INT32_UNSIGNED(ha->HeaderData[0]);
                if(bIsWarcraft3Map == false && (dwFlags & MPQ_OPEN_FORCE_MPQ_V1) == 0)
                {
                    if(ha->pUserData == NULL && dwHeaderID == ID_MPQ_USERDATA)
                    {
                        // Verify if this looks like a valid user data
                        pUserData = IsValidMpqUserData(SearchOffset, FileSize, ha->HeaderData);
                        if(pUserData != NULL)
                        {
                            // Fill the user data header
                            ha->UserDataPos = SearchOffset;
                            ha->pUserData = &ha->UserData;
                            memcpy(ha->pUserData, pUserData, sizeof(TMPQUserData));

                            // Continue searching from that position
                            SearchOffset += ha->pUserData->dwHeaderOffs;
                            break;
                        }
                    }
                }

                // There must be MPQ header signature. Note that STORM.dll from Warcraft III actually
                // tests the MPQ header size. It must be at least 0x20 bytes in order to load it
                // Abused by Spazzler Map protector. Note that the size check is not present
                // in Storm.dll v 1.00, so Diablo I code would load the MPQ anyway.
                dwHeaderSize = BSWAP_INT32_UNSIGNED(ha->HeaderData[1]);
                if(dwHeaderID == ID_MPQ && dwHeaderSize >= MPQ_HEADER_SIZE_V1)
                {
                    // Now convert the header to version 4
                    nError = ConvertMpqHeaderToFormat4(ha, SearchOffset, FileSize, dwFlags);
                    bSearchComplete = true;
                    break;
                }

                // Check for MPK archives (Longwu Online - MPQ fork)
                if(dwHeaderID == ID_MPK)
                {
                    // Now convert the MPK header to MPQ Header version 4
                    nError = ConvertMpkHeaderToFormat4(ha, FileSize, dwFlags);
                    bSearchComplete = true;
                    break;
                }

                // If searching for the MPQ header is disabled, return an error
                if(dwFlags & MPQ_OPEN_NO_HEADER_SEARCH)
                {
                    nError = ERROR_NOT_SUPPORTED;
                    bSearchComplete = true;
                    break;
                }

                // Move the pointers
                SearchOffset += 0x200;
                dwInBufferOffset += 0x200;
            }
        }

        // Did we identify one of the supported headers?
        if(nError == ERROR_SUCCESS)
        {
            // Set the user data position to the MPQ header, if none
            if(ha->pUserData == NULL)
                ha->UserDataPos = SearchOffset;

            // Set the position of the MPQ header
            ha->pHeader  = (TMPQHeader *)ha->HeaderData;
            ha->MpqPos   = SearchOffset;
            ha->FileSize = FileSize;

            // Sector size must be nonzero.
            if(SearchOffset >= FileSize || ha->pHeader->wSectorSize == 0)
                nError = ERROR_BAD_FORMAT;
        }
    }

    // Fix table positions according to format
    if(nError == ERROR_SUCCESS)
    {
        // Dump the header
//      DumpMpqHeader(ha->pHeader);

        // W3x Map Protectors use the fact that War3's Storm.dll ignores the MPQ user data,
        // and ignores the MPQ format version as well. The trick is to
        // fake MPQ format 2, with an improper hi-word position of hash table and block table
        // We can overcome such protectors by forcing opening the archive as MPQ v 1.0
        if(dwFlags & MPQ_OPEN_FORCE_MPQ_V1)
        {
            ha->pHeader->wFormatVersion = MPQ_FORMAT_VERSION_1;
            ha->pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V1;
            ha->dwFlags |= MPQ_FLAG_READ_ONLY;
            ha->pUserData = NULL;
        }

        // Both MPQ_OPEN_NO_LISTFILE or MPQ_OPEN_NO_ATTRIBUTES trigger read only mode
        if(dwFlags & (MPQ_OPEN_NO_LISTFILE | MPQ_OPEN_NO_ATTRIBUTES))
            ha->dwFlags |= MPQ_FLAG_READ_ONLY;

        // Remember whether whis is a map for Warcraft III
        if(bIsWarcraft3Map)
            ha->dwFlags |= MPQ_FLAG_WAR3_MAP;

        // Set the size of file sector
        ha->dwSectorSize = (0x200 << ha->pHeader->wSectorSize);

        // Verify if any of the tables doesn't start beyond the end of the file
        nError = VerifyMpqTablePositions(ha, FileSize);
    }

    // Read the hash table. Ignore the result, as hash table is no longer required
    // Read HET table. Ignore the result, as HET table is no longer required
    if(nError == ERROR_SUCCESS)
    {
        nError = LoadAnyHashTable(ha);
    }

    // Now, build the file table. It will be built by combining
    // the block table, BET table, hi-block table, (attributes) and (listfile).
    if(nError == ERROR_SUCCESS)
    {
        nError = BuildFileTable(ha);
    }

    // Load the internal listfile and include it to the file table
    if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_LISTFILE) == 0)
    {
        // Quick check for (listfile)
        pFileEntry = GetFileEntryLocale(ha, LISTFILE_NAME, LANG_NEUTRAL);
        if(pFileEntry != NULL)
        {
            // Ignore result of the operation. (listfile) is optional.
            SFileAddListFile((HANDLE)ha, NULL);
            ha->dwFileFlags1 = pFileEntry->dwFlags;
        }
    }

    // Load the "(attributes)" file and merge it to the file table
    if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_ATTRIBUTES) == 0 && (ha->dwFlags & MPQ_FLAG_BLOCK_TABLE_CUT) == 0)
    {
        // Quick check for (attributes)
        pFileEntry = GetFileEntryLocale(ha, ATTRIBUTES_NAME, LANG_NEUTRAL);
        if(pFileEntry != NULL)
        {
            // Ignore result of the operation. (attributes) is optional.
            SAttrLoadAttributes(ha);
            ha->dwFileFlags2 = pFileEntry->dwFlags;
        }
    }

    // Remember whether the archive has weak signature. Only for MPQs format 1.0.
    if(nError == ERROR_SUCCESS)
    {
        // Quick check for (signature)
        pFileEntry = GetFileEntryLocale(ha, SIGNATURE_NAME, LANG_NEUTRAL);
        if(pFileEntry != NULL)
        {
            // Just remember that the archive is weak-signed
            assert((pFileEntry->dwFlags & MPQ_FILE_EXISTS) != 0);
            ha->dwFileFlags3 = pFileEntry->dwFlags;
        }

        // Finally, set the MPQ_FLAG_READ_ONLY if the MPQ was found malformed
        ha->dwFlags |= (ha->dwFlags & MPQ_FLAG_MALFORMED) ? MPQ_FLAG_READ_ONLY : 0;
    }

    // Cleanup and exit
    if(nError != ERROR_SUCCESS)
    {
        FileStream_Close(pStream);
        FreeArchiveHandle(ha);
        SetLastError(nError);
        ha = NULL;
    }

    // Free the header buffer
    if(pbHeaderBuffer != NULL)
        STORM_FREE(pbHeaderBuffer);
    if(phMpq != NULL)
        *phMpq = ha;
    return (nError == ERROR_SUCCESS);
}
Exemplo n.º 3
0
bool WINAPI SFileOpenArchive(
    const TCHAR * szMpqName,
    DWORD dwPriority,
    DWORD dwFlags,
    HANDLE * phMpq)
{
    TMPQUserData * pUserData;
    TFileStream * pStream = NULL;       // Open file stream
    TMPQArchive * ha = NULL;            // Archive handle
    TFileEntry * pFileEntry;
    ULONGLONG FileSize = 0;             // Size of the file
    int nError = ERROR_SUCCESS;   

    // Verify the parameters
    if(szMpqName == NULL || *szMpqName == 0 || phMpq == NULL)
        nError = ERROR_INVALID_PARAMETER;

    // One time initialization of MPQ cryptography
    InitializeMpqCryptography();
    dwPriority = dwPriority;

    // Open the MPQ archive file
    if(nError == ERROR_SUCCESS)
    {
        DWORD dwStreamFlags = (dwFlags & STREAM_FLAGS_MASK);

        // If not forcing MPQ v 1.0, also use file bitmap
        dwStreamFlags |= (dwFlags & MPQ_OPEN_FORCE_MPQ_V1) ? 0 : STREAM_FLAG_USE_BITMAP;

        // Initialize the stream
        pStream = FileStream_OpenFile(szMpqName, dwStreamFlags);
        if(pStream == NULL)
            nError = GetLastError();
    }

    // Check the file size. There must be at least 0x20 bytes
    if(nError == ERROR_SUCCESS)
    {
        FileStream_GetSize(pStream, &FileSize);
        if(FileSize < MPQ_HEADER_SIZE_V1)
            nError = ERROR_BAD_FORMAT;
    }

    // Allocate the MPQhandle
    if(nError == ERROR_SUCCESS)
    {
        if((ha = STORM_ALLOC(TMPQArchive, 1)) == NULL)
            nError = ERROR_NOT_ENOUGH_MEMORY;
    }

    // Initialize handle structure and allocate structure for MPQ header
    if(nError == ERROR_SUCCESS)
    {
        ULONGLONG SearchOffset = 0;
        DWORD dwStreamFlags = 0;
        DWORD dwHeaderSize;
        DWORD dwHeaderID;

        memset(ha, 0, sizeof(TMPQArchive));
        ha->pfnHashString = HashString;
        ha->pStream = pStream;
        pStream = NULL;

        // Set the archive read only if the stream is read-only
        FileStream_GetFlags(ha->pStream, &dwStreamFlags);
        ha->dwFlags |= (dwStreamFlags & STREAM_FLAG_READ_ONLY) ? MPQ_FLAG_READ_ONLY : 0;

        // Also remember if we shall check sector CRCs when reading file
        if(dwFlags & MPQ_OPEN_CHECK_SECTOR_CRC)
            ha->dwFlags |= MPQ_FLAG_CHECK_SECTOR_CRC;

        // Find the offset of MPQ header within the file
        while(SearchOffset < FileSize)
        {
            DWORD dwBytesAvailable = MPQ_HEADER_SIZE_V4;

            // Cut the bytes available, if needed
            if((FileSize - SearchOffset) < MPQ_HEADER_SIZE_V4)
                dwBytesAvailable = (DWORD)(FileSize - SearchOffset);

            // Read the eventual MPQ header
            if(!FileStream_Read(ha->pStream, &SearchOffset, ha->HeaderData, dwBytesAvailable))
            {
                nError = GetLastError();
                break;
            }

            // There are AVI files from Warcraft III with 'MPQ' extension.
            if(SearchOffset == 0 && IsAviFile(ha->HeaderData))
            {
                nError = ERROR_AVI_FILE;
                break;
            }

            // If there is the MPQ user data signature, process it
            dwHeaderID = BSWAP_INT32_UNSIGNED(ha->HeaderData[0]);
            if(dwHeaderID == ID_MPQ_USERDATA && ha->pUserData == NULL && (dwFlags & MPQ_OPEN_FORCE_MPQ_V1) == 0)
            {
                // Verify if this looks like a valid user data
                pUserData = IsValidMpqUserData(SearchOffset, FileSize, ha->HeaderData);
                if(pUserData != NULL)
                {
                    // Fill the user data header
                    ha->UserDataPos = SearchOffset;
                    ha->pUserData = &ha->UserData;
                    memcpy(ha->pUserData, pUserData, sizeof(TMPQUserData));

                    // Continue searching from that position
                    SearchOffset += ha->pUserData->dwHeaderOffs;
                    continue;
                }
            }

            // There must be MPQ header signature. Note that STORM.dll from Warcraft III actually
            // tests the MPQ header size. It must be at least 0x20 bytes in order to load it
            // Abused by Spazzler Map protector. Note that the size check is not present
            // in Storm.dll v 1.00, so Diablo I code would load the MPQ anyway.
            dwHeaderSize = BSWAP_INT32_UNSIGNED(ha->HeaderData[1]);
            if(dwHeaderID == ID_MPQ && dwHeaderSize >= MPQ_HEADER_SIZE_V1)
            {
                // Now convert the header to version 4
                nError = ConvertMpqHeaderToFormat4(ha, SearchOffset, FileSize, dwFlags);
                break;
            }

            // Check for MPK archives (Longwu Online - MPQ fork)
            if(dwHeaderID == ID_MPK)
            {
                // Now convert the MPK header to MPQ Header version 4
                nError = ConvertMpkHeaderToFormat4(ha, FileSize, dwFlags);
                break;
            }

            // If searching for the MPQ header is disabled, return an error
            if(dwFlags & MPQ_OPEN_NO_HEADER_SEARCH)
            {
                nError = ERROR_NOT_SUPPORTED;
                break;
            }

            // Move to the next possible offset
            SearchOffset += 0x200;
        }

        // Did we identify one of the supported headers?
        if(nError == ERROR_SUCCESS)
        {
            // Set the user data position to the MPQ header, if none
            if(ha->pUserData == NULL)
                ha->UserDataPos = SearchOffset;

            // Set the position of the MPQ header
            ha->pHeader = (TMPQHeader *)ha->HeaderData;
            ha->MpqPos = SearchOffset;

            // Sector size must be nonzero.
            if(SearchOffset >= FileSize || ha->pHeader->wSectorSize == 0)
                nError = ERROR_BAD_FORMAT;
        }
    }

    // Fix table positions according to format
    if(nError == ERROR_SUCCESS)
    {
        // Dump the header
//      DumpMpqHeader(ha->pHeader);

        // W3x Map Protectors use the fact that War3's Storm.dll ignores the MPQ user data,
        // and ignores the MPQ format version as well. The trick is to
        // fake MPQ format 2, with an improper hi-word position of hash table and block table
        // We can overcome such protectors by forcing opening the archive as MPQ v 1.0
        if(dwFlags & MPQ_OPEN_FORCE_MPQ_V1)
        {
            ha->pHeader->wFormatVersion = MPQ_FORMAT_VERSION_1;
            ha->pHeader->dwHeaderSize = MPQ_HEADER_SIZE_V1;
            ha->dwFlags |= MPQ_FLAG_READ_ONLY;
            ha->pUserData = NULL;
        }

        // Both MPQ_OPEN_NO_LISTFILE or MPQ_OPEN_NO_ATTRIBUTES trigger read only mode
        if(dwFlags & (MPQ_OPEN_NO_LISTFILE | MPQ_OPEN_NO_ATTRIBUTES))
            ha->dwFlags |= MPQ_FLAG_READ_ONLY;

        // Set the size of file sector
        ha->dwSectorSize = (0x200 << ha->pHeader->wSectorSize);

        // Verify if any of the tables doesn't start beyond the end of the file
        nError = VerifyMpqTablePositions(ha, FileSize);
    }

    // Read the hash table. Ignore the result, as hash table is no longer required
    // Read HET table. Ignore the result, as HET table is no longer required
    if(nError == ERROR_SUCCESS)
    {
        nError = LoadAnyHashTable(ha);
    }

    // Now, build the file table. It will be built by combining
    // the block table, BET table, hi-block table, (attributes) and (listfile).
    if(nError == ERROR_SUCCESS)
    {
        nError = BuildFileTable(ha);
    }

    // Verify the file table, if no kind of malformation was detected
    if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_MALFORMED) == 0)
    {
        TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
        ULONGLONG RawFilePos;

        // Parse all file entries
        for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
        {
            // If that file entry is valid, check the file position
            if(pFileEntry->dwFlags & MPQ_FILE_EXISTS)
            {
                // Get the 64-bit file position,
                // relative to the begin of the file
                RawFilePos = ha->MpqPos + pFileEntry->ByteOffset;

                // Begin of the file must be within range
                if(RawFilePos > FileSize)
                {
                    nError = ERROR_FILE_CORRUPT;
                    break;
                }

                // End of the file must be within range
                RawFilePos += pFileEntry->dwCmpSize;
                if(RawFilePos > FileSize)
                {
                    nError = ERROR_FILE_CORRUPT;
                    break;
                }
            }
        }
    }

    // Load the internal listfile and include it to the file table
    if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_LISTFILE) == 0)
    {
        // Save the flags for (listfile)
        pFileEntry = GetFileEntryLocale(ha, LISTFILE_NAME, LANG_NEUTRAL);
        if(pFileEntry != NULL)
        {
            // Ignore result of the operation. (listfile) is optional.
            SFileAddListFile((HANDLE)ha, NULL);
            ha->dwFileFlags1 = pFileEntry->dwFlags;
        }
    }

    // Load the "(attributes)" file and merge it to the file table
    if(nError == ERROR_SUCCESS && (dwFlags & MPQ_OPEN_NO_ATTRIBUTES) == 0)
    {
        // Save the flags for (attributes)
        pFileEntry = GetFileEntryLocale(ha, ATTRIBUTES_NAME, LANG_NEUTRAL);
        if(pFileEntry != NULL)
        {
            // Ignore result of the operation. (attributes) is optional.
            SAttrLoadAttributes(ha);
            ha->dwFileFlags2 = pFileEntry->dwFlags;
        }
    }

    // Cleanup and exit
    if(nError != ERROR_SUCCESS)
    {
        FileStream_Close(pStream);
        FreeMPQArchive(ha);
        SetLastError(nError);
        ha = NULL;
    }

    *phMpq = ha;
    return (nError == ERROR_SUCCESS);
}