예제 #1
0
// When this function is called, it is already ensured that the parameters are valid
// (e.g. the "dwToRead + dwFilePos" is not greater than the file size)
// TODO: Test for archives > 4GB
static DWORD WINAPI ReadMPQFileSingleUnit(TMPQFile * hf, DWORD dwFilePos, BYTE * pbBuffer, DWORD dwToRead)
{
    TMPQArchive * ha = hf->ha;
    DWORD dwBytesRead = 0;

    if(ha->FilePointer.QuadPart != hf->MpqFilePos.QuadPart)
    {
        SetFilePointer(ha->hFile, hf->MpqFilePos.LowPart, &hf->MpqFilePos.HighPart, FILE_BEGIN);
        ha->FilePointer = hf->MpqFilePos;
    }

    // If the file is really compressed, decompress it.
    // Otherwise, read the data as-is to the caller.
    if(hf->pBlock->dwCSize < hf->pBlock->dwFSize)
    {
        if(hf->pbFileBuffer == NULL)
        {
            BYTE * inputBuffer = NULL;
            int outputBufferSize = (int)hf->pBlock->dwFSize;
            int inputBufferSize = (int)hf->pBlock->dwCSize;

            hf->pbFileBuffer = ALLOCMEM(BYTE, outputBufferSize);
            inputBuffer = ALLOCMEM(BYTE, inputBufferSize);
            if(inputBuffer != NULL && hf->pbFileBuffer != NULL)
            {
                // Read the compressed file data
                ReadFile(ha->hFile, inputBuffer, inputBufferSize, &dwBytesRead, NULL);

                // Is the file compressed with PKWARE Data Compression Library ?
                if(hf->pBlock->dwFlags & MPQ_FILE_COMPRESS_PKWARE)
                    Decompress_pklib((char *)hf->pbFileBuffer, &outputBufferSize, (char *)inputBuffer, (int)inputBufferSize);

                // Is it a file compressed by Blizzard's multiple compression ?
                // Note that Storm.dll v 1.0.9 distributed with Warcraft III
                // passes the full path name of the opened archive as the new last parameter
                if(hf->pBlock->dwFlags & MPQ_FILE_COMPRESS_MULTI)
                    SCompDecompress((char *)hf->pbFileBuffer, &outputBufferSize, (char *)inputBuffer, (int)inputBufferSize);
            }

            // Free the temporary buffer
            if(inputBuffer != NULL)
                FREEMEM(inputBuffer);
        }

        // Copy the file data, if any there
        if(hf->pbFileBuffer != NULL)
        {
            memcpy(pbBuffer, hf->pbFileBuffer + dwFilePos, dwToRead);
            dwBytesRead += dwToRead;
        }
    }
    else
    {
        // Read the uncompressed file data
        ReadFile(ha->hFile, pbBuffer, dwToRead, &dwBytesRead, NULL);
        dwBytesRead = (int)dwBytesRead;
    }

    return (DWORD)dwBytesRead;
}
예제 #2
0
JNIH_EXCEPTION_TRAP_BEGIN() {
    JByteArray jInBuffer(env, inBuffer);
    JByteArray tempBuffer(env, outBufferLength);
    jint decompressedSize = outBufferLength;

    jboolean result = SCompDecompress(
        tempBuffer.getData(),
        reinterpret_cast<int*>(&decompressedSize),
        jInBuffer.getData(),
        inBufferLength
    );

    if (!result) {
        ErrorCodeToException(env, GetLastError());
    } else {
        JByteArray outBuffer(env, decompressedSize);
        memcpy(outBuffer.getData(), tempBuffer.getData(), decompressedSize);

        return outBuffer.getArray();
    }
} JNIH_EXCEPTION_TRAP_END
예제 #3
0
//  hf            - MPQ File handle.
//  pbBuffer      - Pointer to target buffer to store sectors.
//  dwByteOffset  - Position of sector in the file (relative to file begin)
//  dwBytesToRead - Number of bytes to read. Must be multiplier of sector size.
//  pdwBytesRead  - Stored number of bytes loaded
static int ReadMpqSectors(TMPQFile * hf, LPBYTE pbBuffer, DWORD dwByteOffset, DWORD dwBytesToRead, LPDWORD pdwBytesRead)
{
    ULONGLONG RawFilePos;
    TMPQArchive * ha = hf->ha;
    TFileEntry * pFileEntry = hf->pFileEntry;
    LPBYTE pbRawSector = NULL;
    LPBYTE pbOutSector = pbBuffer;
    LPBYTE pbInSector = pbBuffer;
    DWORD dwRawBytesToRead;
    DWORD dwRawSectorOffset = dwByteOffset;
    DWORD dwSectorsToRead = dwBytesToRead / ha->dwSectorSize;
    DWORD dwSectorIndex = dwByteOffset / ha->dwSectorSize;
    DWORD dwSectorsDone = 0;
    DWORD dwBytesRead = 0;
    int nError = ERROR_SUCCESS;

    // Note that dwByteOffset must be aligned to size of one sector
    // Note that dwBytesToRead must be a multiplier of one sector size
    // This is local function, so we won't check if that's true.
    // Note that files stored in single units are processed by a separate function

    // If there is not enough bytes remaining, cut dwBytesToRead
    if((dwByteOffset + dwBytesToRead) > hf->dwDataSize)
        dwBytesToRead = hf->dwDataSize - dwByteOffset;
    dwRawBytesToRead = dwBytesToRead;

    // Perform all necessary work to do with compressed files
    if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED)
    {
        // If the sector positions are not loaded yet, do it
        if(hf->SectorOffsets == NULL)
        {
            nError = AllocateSectorOffsets(hf, true);
            if(nError != ERROR_SUCCESS)
                return nError;
        }

        // If the sector checksums are not loaded yet, load them now.
        if(hf->SectorChksums == NULL && (pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC))
        {
            nError = AllocateSectorChecksums(hf, true);
            if(nError != ERROR_SUCCESS)
                return nError;
        }

        // If the file is compressed, also allocate secondary buffer
        pbInSector = pbRawSector = ALLOCMEM(BYTE, dwBytesToRead);
        if(pbRawSector == NULL)
            return ERROR_NOT_ENOUGH_MEMORY;

        // Assign the temporary buffer as target for read operation
        dwRawSectorOffset = hf->SectorOffsets[dwSectorIndex];
        dwRawBytesToRead = hf->SectorOffsets[dwSectorIndex + dwSectorsToRead] - dwRawSectorOffset;
    }

    // Calculate raw file offset where the sector(s) are stored.
    CalculateRawSectorOffset(RawFilePos, hf, dwRawSectorOffset);

    // Set file pointer and read all required sectors
    if(!FileStream_Read(ha->pStream, &RawFilePos, pbInSector, dwRawBytesToRead))
        return GetLastError();
    dwBytesRead = 0;

    // Now we have to decrypt and decompress all file sectors that have been loaded
    for(DWORD i = 0; i < dwSectorsToRead; i++)
    {
        DWORD dwRawBytesInThisSector = ha->dwSectorSize;
        DWORD dwBytesInThisSector = ha->dwSectorSize;
        DWORD dwIndex = dwSectorIndex + i;

        // If there is not enough bytes in the last sector,
        // cut the number of bytes in this sector
        if(dwRawBytesInThisSector > dwBytesToRead)
            dwRawBytesInThisSector = dwBytesToRead;
        if(dwBytesInThisSector > dwBytesToRead)
            dwBytesInThisSector = dwBytesToRead;

        // If the file is compressed, we have to adjust the raw sector size
        if(pFileEntry->dwFlags & MPQ_FILE_COMPRESSED)
            dwRawBytesInThisSector = hf->SectorOffsets[dwIndex + 1] - hf->SectorOffsets[dwIndex];

        // If the file is encrypted, we have to decrypt the sector
        if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
        {
            BSWAP_ARRAY32_UNSIGNED(pbInSector, dwRawBytesInThisSector);

            // If we don't know the key, try to detect it by file content
            if(hf->dwFileKey == 0)
            {
                hf->dwFileKey = DetectFileKeyByContent(pbInSector, dwBytesInThisSector);
                if(hf->dwFileKey == 0)
                {
                    nError = ERROR_UNKNOWN_FILE_KEY;
                    break;
                }
            }

            DecryptMpqBlock(pbInSector, dwRawBytesInThisSector, hf->dwFileKey + dwIndex);
            BSWAP_ARRAY32_UNSIGNED(pbInSector, dwRawBytesInThisSector);
        }

        // If the file has sector CRC check turned on, perform it
        if(hf->bCheckSectorCRCs && hf->SectorChksums != NULL)
        {
            DWORD dwAdlerExpected = hf->SectorChksums[dwIndex];
            DWORD dwAdlerValue = 0;

            // We can only check sector CRC when it's not zero
            // Neither can we check it if it's 0xFFFFFFFF.
            if(dwAdlerExpected != 0 && dwAdlerExpected != 0xFFFFFFFF)
            {
                dwAdlerValue = adler32(0, pbInSector, dwRawBytesInThisSector);
                if(dwAdlerValue != dwAdlerExpected)
                {
                    nError = ERROR_CHECKSUM_ERROR;
                    break;
                }
            }
        }

        // If the sector is really compressed, decompress it.
        // WARNING : Some sectors may not be compressed, it can be determined only
        // by comparing uncompressed and compressed size !!!
        if(dwRawBytesInThisSector < dwBytesInThisSector)
        {
            int cbOutSector = dwBytesInThisSector;
            int cbInSector = dwRawBytesInThisSector;
            int nResult = 0;

            // Is the file compressed by PKWARE Data Compression Library ?
            if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE)
                nResult = SCompExplode((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector);

            // Is the file compressed by Blizzard's multiple compression ?
            if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS)
                nResult = SCompDecompress((char *)pbOutSector, &cbOutSector, (char *)pbInSector, cbInSector);

            // Did the decompression fail ?
            if(nResult == 0)
            {
                nError = ERROR_FILE_CORRUPT;
                break;
            }
        }
        else
        {
            if(pbOutSector != pbInSector)
                memcpy(pbOutSector, pbInSector, dwBytesInThisSector);
        }

        // Move pointers
        dwBytesToRead -= dwBytesInThisSector;
        dwByteOffset += dwBytesInThisSector;
        dwBytesRead += dwBytesInThisSector;
        pbOutSector += dwBytesInThisSector;
        pbInSector += dwRawBytesInThisSector;
        dwSectorsDone++;
    }

    // Free all used buffers
    if(pbRawSector != NULL)
        FREEMEM(pbRawSector);
    
    // Give the caller thenumber of bytes read
    *pdwBytesRead = dwBytesRead;
    return nError; 
}
예제 #4
0
static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwToRead, LPDWORD pdwBytesRead)
{
    ULONGLONG RawFilePos = hf->RawFilePos;
    TMPQArchive * ha = hf->ha;
    TFileEntry * pFileEntry = hf->pFileEntry;
    LPBYTE pbCompressed = NULL;
    LPBYTE pbRawData = NULL;
    int nError;

    // If the file buffer is not allocated yet, do it.
    if(hf->pbFileSector == NULL)
    {
        nError = AllocateSectorBuffer(hf);
        if(nError != ERROR_SUCCESS)
            return nError;
        pbRawData = hf->pbFileSector;
    }

    // If the file is a patch file, adjust raw data offset
    if(hf->pPatchInfo != NULL)
        RawFilePos += hf->pPatchInfo->dwLength;

    // If the file buffer is not loaded yet, do it
    if(hf->dwSectorOffs != 0)
    {
        //
        // In "wow-update-12694.MPQ" from Wow-Cataclysm BETA:
        //
        // File                                    CmpSize FileSize  Data
        // --------------------------------------  ------- --------  ---------------
        // esES\DBFilesClient\LightSkyBox.dbc      0xBE    0xBC      Is compressed
        // deDE\DBFilesClient\MountCapability.dbc  0x93    0x77      Is uncompressed
        // 
        // Now tell me how to deal with this mess. Apparently
        // someone made a mistake at Blizzard ...
        //

        if(hf->pPatchInfo != NULL)
        {
            // Allocate space for 
            pbCompressed = ALLOCMEM(BYTE, pFileEntry->dwCmpSize);
            if(pbCompressed == NULL)
                return ERROR_NOT_ENOUGH_MEMORY;

            // Read the entire file
            if(!FileStream_Read(ha->pStream, &RawFilePos, pbCompressed, pFileEntry->dwCmpSize))
            {
                FREEMEM(pbCompressed);
                return GetLastError();
            }

            // We assume that patch files are not encrypted
            assert((pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) == 0);
            assert((pFileEntry->dwFlags & MPQ_FILE_IMPLODE) == 0);

            // Check the 'PTCH' signature to find out if it's compressed or not
            if(pbCompressed[0] != 'P' || pbCompressed[1] != 'T' || pbCompressed[2] != 'C' || pbCompressed[3] != 'H')
            {
                int cbOutBuffer = (int)hf->dwDataSize;
                int nResult = SCompDecompress((char *)hf->pbFileSector,
                                                     &cbOutBuffer,
                                              (char *)pbCompressed,
                                                 (int)pFileEntry->dwCmpSize);
                if(nResult == 0)
                {
                    FREEMEM(pbCompressed);
                    return ERROR_FILE_CORRUPT;
                }
            }
            else
            {
                memcpy(hf->pbFileSector, pbCompressed, hf->dwDataSize);
            }

            // Free the decompression buffer.
            FREEMEM(pbCompressed);
        }
        else
        {
            // If the file is compressed, we have to allocate buffer for compressed data
            if(pFileEntry->dwCmpSize < hf->dwDataSize)
            {
                pbCompressed = ALLOCMEM(BYTE, pFileEntry->dwCmpSize);
                if(pbCompressed == NULL)
                    return ERROR_NOT_ENOUGH_MEMORY;
                pbRawData = pbCompressed;
            }

            // Read the entire file
            if(!FileStream_Read(ha->pStream, &RawFilePos, pbRawData, pFileEntry->dwCmpSize))
            {
                FREEMEM(pbCompressed);
                return GetLastError();
            }

            // If the file is encrypted, we have to decrypt the data first
            if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
            {
                BSWAP_ARRAY32_UNSIGNED(pbRawData, pFileEntry->dwCmpSize);
                DecryptMpqBlock(pbRawData, pFileEntry->dwCmpSize, hf->dwFileKey);
                BSWAP_ARRAY32_UNSIGNED(pbRawData, pFileEntry->dwCmpSize);
            }

            // If the file is compressed, we have to decompress it now
            if(pFileEntry->dwCmpSize < hf->dwDataSize)
            {
                int cbOutBuffer = (int)hf->dwDataSize;
                int nResult = 0;

                // Note: Single unit files compressed with IMPLODE are not supported by Blizzard
                if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE)
                    nResult = SCompExplode((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, (int)pFileEntry->dwCmpSize);
                if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS)
                    nResult = SCompDecompress((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, (int)pFileEntry->dwCmpSize);

                // Free the decompression buffer.
                FREEMEM(pbCompressed);
                if(nResult == 0)
                    return ERROR_FILE_CORRUPT;
            }
        }

        // The file sector is now properly loaded
        hf->dwSectorOffs = 0;
    }

    // At this moment, we have the file loaded into the file buffer.
    // Copy as much as the caller wants
    if(hf->dwSectorOffs == 0)
    {
        // File position is greater or equal to file size ?
        if(hf->dwFilePos >= hf->dwDataSize)
        {
            *pdwBytesRead = 0;
            return ERROR_SUCCESS;
        }

        // If not enough bytes remaining in the file, cut them
        if((hf->dwDataSize - hf->dwFilePos) < dwToRead)
            dwToRead = (hf->dwDataSize - hf->dwFilePos);

        // Copy the bytes
        memcpy(pvBuffer, hf->pbFileSector + hf->dwFilePos, dwToRead);
        hf->dwFilePos += dwToRead;

        // Give the number of bytes read
        *pdwBytesRead = dwToRead;
        return ERROR_SUCCESS;
    }

    // An error, sorry
    return ERROR_CAN_NOT_COMPLETE;
}
예제 #5
0
static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead)
{
    ULONGLONG RawFilePos = hf->RawFilePos;
    TMPQArchive * ha = hf->ha;
    TFileEntry * pFileEntry = hf->pFileEntry;
    LPBYTE pbCompressed = NULL;
    LPBYTE pbRawData = NULL;
    bool bIsReallyCompressed = false;
    int nError = ERROR_SUCCESS;

    // If the file buffer is not allocated yet, do it.
    if(hf->pbFileSector == NULL)
    {
        nError = AllocateSectorBuffer(hf);
        if(nError != ERROR_SUCCESS)
            return nError;
        pbRawData = hf->pbFileSector;
    }

    // If the file is a patch file, adjust raw data offset
    if(hf->pPatchInfo != NULL)
        RawFilePos += hf->pPatchInfo->dwLength;

    // If the file sector is not loaded yet, do it
    if(hf->dwSectorOffs != 0)
    {
        // Is the file compressed?
        if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS)
        {
            // Allocate space for compressed data
            pbCompressed = STORM_ALLOC(BYTE, pFileEntry->dwCmpSize);
            if(pbCompressed == NULL)
                return ERROR_NOT_ENOUGH_MEMORY;
            bIsReallyCompressed = true;
            pbRawData = pbCompressed;
        }

        // Load the raw (compressed, encrypted) data
        if(!FileStream_Read(ha->pStream, &RawFilePos, pbRawData, pFileEntry->dwCmpSize))
        {
            STORM_FREE(pbCompressed);
            return GetLastError();
        }

        // If the file is encrypted, we have to decrypt the data first
        if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
        {
            BSWAP_ARRAY32_UNSIGNED(pbRawData, pFileEntry->dwCmpSize);
            DecryptMpqBlock(pbRawData, pFileEntry->dwCmpSize, hf->dwFileKey);
            BSWAP_ARRAY32_UNSIGNED(pbRawData, pFileEntry->dwCmpSize);
        }

        //
        // In "wow-update-12694.MPQ" from Wow-Cataclysm BETA:
        //
        // File                                    CmpSize FileSize  Data
        // --------------------------------------  ------- --------  ---------------
        // esES\DBFilesClient\LightSkyBox.dbc      0xBE    0xBC      Is compressed
        // deDE\DBFilesClient\MountCapability.dbc  0x93    0x77      Is uncompressed
        //
        // Now tell me how to deal with this mess.
        //

        if(hf->pPatchInfo != NULL)
        {
            if(pbRawData[0] == 'P' && pbRawData[1] == 'T' && pbRawData[2] == 'C' && pbRawData[3] == 'H')
            {
                assert(pFileEntry->dwCmpSize >= hf->dwDataSize);
                bIsReallyCompressed = false;
            }
        }
        else
        {
            if(pFileEntry->dwCmpSize >= hf->dwDataSize)
                bIsReallyCompressed = false;
        }

        // If the file is compressed, we have to decompress it now
        if(bIsReallyCompressed)
        {
            int cbOutBuffer = (int)hf->dwDataSize;

            // Note: Single unit files compressed with IMPLODE are not supported by Blizzard
            if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE)
            {
                if(!SCompExplode((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, (int)pFileEntry->dwCmpSize))
                    nError = ERROR_FILE_CORRUPT;
            }
            if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS)
            {
                if(!SCompDecompress((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, (int)pFileEntry->dwCmpSize))
                    nError = ERROR_FILE_CORRUPT;
            }
        }
        else
        {
            if(pbRawData != hf->pbFileSector)
                memcpy(hf->pbFileSector, pbRawData, hf->dwDataSize);
        }

        // Free the decompression buffer.
        if(pbCompressed != NULL)
            STORM_FREE(pbCompressed);

        // The file sector is now properly loaded
        hf->dwSectorOffs = 0;
    }

    // At this moment, we have the file loaded into the file buffer.
    // Copy as much as the caller wants
    if(nError == ERROR_SUCCESS && hf->dwSectorOffs == 0)
    {
        // File position is greater or equal to file size ?
        if(dwFilePos >= hf->dwDataSize)
        {
            *pdwBytesRead = 0;
            return ERROR_SUCCESS;
        }

        // If not enough bytes remaining in the file, cut them
        if((hf->dwDataSize - dwFilePos) < dwToRead)
            dwToRead = (hf->dwDataSize - dwFilePos);

        // Copy the bytes
        memcpy(pvBuffer, hf->pbFileSector + dwFilePos, dwToRead);

        // Give the number of bytes read
        *pdwBytesRead = dwToRead;
        return ERROR_SUCCESS;
    }

    // An error, sorry
    return ERROR_CAN_NOT_COMPLETE;
}
static int ReadMpqFileSingleUnit(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwToRead, LPDWORD pdwBytesRead)
{
    ULONGLONG RawFilePos = hf->RawFilePos;
    TMPQArchive * ha = hf->ha;
    TFileEntry * pFileEntry = hf->pFileEntry;
    LPBYTE pbCompressed = NULL;
    LPBYTE pbRawData;
    int nError = ERROR_SUCCESS;

    // If the file buffer is not allocated yet, do it.
    if(hf->pbFileSector == NULL)
    {
        nError = AllocateSectorBuffer(hf);
        if(nError != ERROR_SUCCESS || hf->pbFileSector == NULL)
            return nError;
    }

    // If the file is a patch file, adjust raw data offset
    if(hf->pPatchInfo != NULL)
        RawFilePos += hf->pPatchInfo->dwLength;
    pbRawData = hf->pbFileSector;

    // If the file sector is not loaded yet, do it
    if(hf->dwSectorOffs != 0)
    {
        // Is the file compressed?
        if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK)
        {
            // Allocate space for compressed data
            pbCompressed = STORM_ALLOC(BYTE, pFileEntry->dwCmpSize);
            if(pbCompressed == NULL)
                return ERROR_NOT_ENOUGH_MEMORY;
            pbRawData = pbCompressed;
        }
        
        // Load the raw (compressed, encrypted) data
        if(!FileStream_Read(ha->pStream, &RawFilePos, pbRawData, pFileEntry->dwCmpSize))
        {
            STORM_FREE(pbCompressed);
            return GetLastError();
        }

        // If the file is encrypted, we have to decrypt the data first
        if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)
        {
            BSWAP_ARRAY32_UNSIGNED(pbRawData, pFileEntry->dwCmpSize);
            DecryptMpqBlock(pbRawData, pFileEntry->dwCmpSize, hf->dwFileKey);
            BSWAP_ARRAY32_UNSIGNED(pbRawData, pFileEntry->dwCmpSize);
        }

        // If the file is compressed, we have to decompress it now
        if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS_MASK)
        {
            int cbOutBuffer = (int)hf->dwDataSize;
            int cbInBuffer = (int)pFileEntry->dwCmpSize;
            int nResult = 0;

            //
            // If the file is an incremental patch, the size of compressed data
            // is determined as pFileEntry->dwCmpSize - sizeof(TPatchInfo)
            //
            // In "wow-update-12694.MPQ" from Wow-Cataclysm BETA:
            //
            // File                                    CmprSize   DcmpSize DataSize Compressed?
            // --------------------------------------  ---------- -------- -------- ---------------
            // esES\DBFilesClient\LightSkyBox.dbc      0xBE->0xA2  0xBC     0xBC     Yes
            // deDE\DBFilesClient\MountCapability.dbc  0x93->0x77  0x77     0x77     No
            // 

            if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE)
                cbInBuffer = cbInBuffer - sizeof(TPatchInfo);

            // Is the file compressed by Blizzard's multiple compression ?
            if(pFileEntry->dwFlags & MPQ_FILE_COMPRESS)
            {
                // Remember the last used compression
                hf->dwCompression0 = pbRawData[0];

                // Decompress the file
                if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2)
                    nResult = SCompDecompress2(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer);
                else
                    nResult = SCompDecompress(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer);
            }

            // Is the file compressed by PKWARE Data Compression Library ?
            // Note: Single unit files compressed with IMPLODE are not supported by Blizzard
            else if(pFileEntry->dwFlags & MPQ_FILE_IMPLODE)
                nResult = SCompExplode(hf->pbFileSector, &cbOutBuffer, pbRawData, cbInBuffer);

            nError = (nResult != 0) ? ERROR_SUCCESS : ERROR_FILE_CORRUPT;
        }
        else
        {
            if(hf->pbFileSector != NULL && pbRawData != hf->pbFileSector)
                memcpy(hf->pbFileSector, pbRawData, hf->dwDataSize);
        }

        // Free the decompression buffer.
        if(pbCompressed != NULL)
            STORM_FREE(pbCompressed);

        // The file sector is now properly loaded
        hf->dwSectorOffs = 0;
    }

    // At this moment, we have the file loaded into the file buffer.
    // Copy as much as the caller wants
    if(nError == ERROR_SUCCESS && hf->dwSectorOffs == 0)
    {
        // File position is greater or equal to file size ?
        if(dwFilePos >= hf->dwDataSize)
        {
            *pdwBytesRead = 0;
            return ERROR_SUCCESS;
        }

        // If not enough bytes remaining in the file, cut them
        if((hf->dwDataSize - dwFilePos) < dwToRead)
            dwToRead = (hf->dwDataSize - dwFilePos);

        // Copy the bytes
        memcpy(pvBuffer, hf->pbFileSector + dwFilePos, dwToRead);

        // Give the number of bytes read
        *pdwBytesRead = dwToRead;
        return ERROR_SUCCESS;
    }

    // An error, sorry
    return ERROR_CAN_NOT_COMPLETE;
}
예제 #7
0
// TODO: Test for archives > 4GB
static DWORD WINAPI ReadMPQBlocks(TMPQFile * hf, DWORD dwBlockPos, BYTE * buffer, DWORD blockBytes)
{
    LARGE_INTEGER FilePos;
    TMPQArchive * ha = hf->ha;          // Archive handle
    BYTE  * tempBuffer = NULL;          // Buffer for reading compressed data from the file
    DWORD   dwFilePos = dwBlockPos;     // Reading position from the file
    DWORD   dwToRead;                   // Number of bytes to read
    DWORD   blockNum;                   // Block number (needed for decrypt)
    DWORD   dwBytesRead = 0;            // Total number of bytes read
    DWORD   bytesRemain = 0;            // Number of data bytes remaining up to the end of the file
    DWORD   nBlocks;                    // Number of blocks to load
    DWORD   i;

    // Test parameters. Block position and block size must be block-aligned, block size nonzero
    if((dwBlockPos & (ha->dwBlockSize - 1)) || blockBytes == 0)
        return 0;

    // Check the end of file
    if((dwBlockPos + blockBytes) > hf->pBlock->dwFSize)
        blockBytes = hf->pBlock->dwFSize - dwBlockPos;

    bytesRemain = hf->pBlock->dwFSize - dwBlockPos;
    blockNum    = dwBlockPos / ha->dwBlockSize;
    nBlocks     = blockBytes / ha->dwBlockSize;
    if(blockBytes % ha->dwBlockSize)
        nBlocks++;

    // If file has variable block positions, we have to load them
    if((hf->pBlock->dwFlags & MPQ_FILE_COMPRESSED) && hf->bBlockPosLoaded == FALSE)
    {
        // Move file pointer to the begin of the file in the MPQ
        if(hf->MpqFilePos.QuadPart != ha->FilePointer.QuadPart)
        {
            SetFilePointer(ha->hFile, hf->MpqFilePos.LowPart, &hf->MpqFilePos.HighPart, FILE_BEGIN);
        }

        // Read block positions from begin of file.
        dwToRead = (hf->nBlocks+1) * sizeof(DWORD);
        if(hf->pBlock->dwFlags & MPQ_FILE_HAS_EXTRA)
            dwToRead += sizeof(DWORD);

        // Read the block pos table and convert the buffer to little endian
        ReadFile(ha->hFile, hf->pdwBlockPos, dwToRead, &dwBytesRead, NULL);
        BSWAP_ARRAY32_UNSIGNED(hf->pdwBlockPos, (hf->nBlocks+1));

        //
        // If the archive if protected some way, perform additional check
        // Sometimes, the file appears not to be encrypted, but it is.
        //
        // Note: In WoW 1.10+, there's a new flag. With this flag present,
        // there's one additional entry in the block table.
        //

        if(hf->pdwBlockPos[0] != dwBytesRead)
            hf->pBlock->dwFlags |= MPQ_FILE_ENCRYPTED;

        // Decrypt loaded block positions if necessary
        if(hf->pBlock->dwFlags & MPQ_FILE_ENCRYPTED)
        {
            // If we don't know the file seed, try to find it.
            if(hf->dwSeed1 == 0)
                hf->dwSeed1 = DetectFileSeed(hf->pdwBlockPos, dwBytesRead);

            // If we don't know the file seed, sorry but we cannot extract the file.
            if(hf->dwSeed1 == 0)
                return 0;

            // Decrypt block positions
            DecryptMPQBlock(hf->pdwBlockPos, dwBytesRead, hf->dwSeed1 - 1);

            // Check if the block positions are correctly decrypted
            // I don't know why, but sometimes it will result invalid block positions on some files
            if(hf->pdwBlockPos[0] != dwBytesRead)
            {
                // Try once again to detect file seed and decrypt the blocks
                // TODO: Test with >4GB
                SetFilePointer(ha->hFile, hf->MpqFilePos.LowPart, &hf->MpqFilePos.HighPart, FILE_BEGIN);
                ReadFile(ha->hFile, hf->pdwBlockPos, dwToRead, &dwBytesRead, NULL);

                BSWAP_ARRAY32_UNSIGNED(hf->pdwBlockPos, (hf->nBlocks+1));
                hf->dwSeed1 = DetectFileSeed(hf->pdwBlockPos, dwBytesRead);
                DecryptMPQBlock(hf->pdwBlockPos, dwBytesRead, hf->dwSeed1 - 1);

                // Check if the block positions are correctly decrypted
                if(hf->pdwBlockPos[0] != dwBytesRead)
                    return 0;
            }
        }

        // Update hf's variables
        ha->FilePointer.QuadPart = hf->MpqFilePos.QuadPart + dwBytesRead;
        hf->bBlockPosLoaded = TRUE;
    }

    // Get file position and number of bytes to read
    dwFilePos = dwBlockPos;
    dwToRead  = blockBytes;
    if(hf->pBlock->dwFlags & MPQ_FILE_COMPRESSED)
    {
        dwFilePos = hf->pdwBlockPos[blockNum];
        dwToRead  = hf->pdwBlockPos[blockNum + nBlocks] - dwFilePos;
    }
    FilePos.QuadPart = hf->MpqFilePos.QuadPart + dwFilePos;

    // Get work buffer for store read data
    tempBuffer = buffer;
    if(hf->pBlock->dwFlags & MPQ_FILE_COMPRESSED)
    {
        if((tempBuffer = ALLOCMEM(BYTE, dwToRead)) == NULL)
        {
            SetLastError(ERROR_NOT_ENOUGH_MEMORY);
            return 0;
        }
    }

    // Set file pointer, if necessary
    if(ha->FilePointer.QuadPart != FilePos.QuadPart)
    {
        SetFilePointer(ha->hFile, FilePos.LowPart, &FilePos.HighPart, FILE_BEGIN);
    }

    // 15018F87 : Read all requested blocks
    ReadFile(ha->hFile, tempBuffer, dwToRead, &dwBytesRead, NULL);
    ha->FilePointer.QuadPart = FilePos.QuadPart + dwBytesRead;

    // Block processing part.
    DWORD blockStart = 0;               // Index of block start in work buffer
    DWORD blockSize  = min(blockBytes, ha->dwBlockSize);
    DWORD index      = blockNum;        // Current block index

    dwBytesRead = 0;                      // Clear read byte counter

    // Walk through all blocks
    for(i = 0; i < nBlocks; i++, index++)
    {
        BYTE * inputBuffer = tempBuffer + blockStart;
        int    outLength = ha->dwBlockSize;

        if(bytesRemain < (DWORD)outLength)
            outLength = bytesRemain;

        // Get current block length
        if(hf->pBlock->dwFlags & MPQ_FILE_COMPRESSED)
            blockSize = hf->pdwBlockPos[index+1] - hf->pdwBlockPos[index];

        // If block is encrypted, we have to decrypt it.
        if(hf->pBlock->dwFlags & MPQ_FILE_ENCRYPTED)
        {
            BSWAP_ARRAY32_UNSIGNED((DWORD *)inputBuffer, blockSize / sizeof(DWORD));

            // If we don't know the seed, try to decode it as WAVE file
            if(hf->dwSeed1 == 0)
                hf->dwSeed1 = DetectFileSeed2((DWORD *)inputBuffer, 3, ID_WAVE, hf->pBlock->dwFSize - 8, 0x45564157);

            // Let's try MSVC's standard EXE or header
            if(hf->dwSeed1 == 0)
                hf->dwSeed1 = DetectFileSeed2((DWORD *)inputBuffer, 2, 0x00905A4D, 0x00000003);

            if(hf->dwSeed1 == 0)
                return 0;

            DecryptMPQBlock((DWORD *)inputBuffer, blockSize, hf->dwSeed1 + index);
            BSWAP_ARRAY32_UNSIGNED((DWORD *)inputBuffer, blockSize / sizeof(DWORD));
        }

        // If the block is really compressed, decompress it.
        // WARNING : Some block may not be compressed, it can be determined only
        // by comparing uncompressed and compressed size !!!
        if(blockSize < (DWORD)outLength)
        {
            // Is the file compressed with PKWARE Data Compression Library ?
            if(hf->pBlock->dwFlags & MPQ_FILE_COMPRESS_PKWARE)
                Decompress_pklib((char *)buffer, &outLength, (char *)inputBuffer, (int)blockSize);

            // Is it a file compressed by Blizzard's multiple compression ?
            // Note that Storm.dll v 1.0.9 distributed with Warcraft III
            // passes the full path name of the opened archive as the new last parameter
            if(hf->pBlock->dwFlags & MPQ_FILE_COMPRESS_MULTI)
                SCompDecompress((char *)buffer, &outLength, (char *)inputBuffer, (int)blockSize);
            dwBytesRead += outLength;
            buffer    += outLength;
        }
        else
        {
            if(buffer != inputBuffer)
                memcpy(buffer, inputBuffer, blockSize);

            dwBytesRead += blockSize;
            buffer    += blockSize;
        }
        blockStart  += blockSize;
        bytesRemain -= outLength;
    }

    // Delete input buffer, if necessary
    if(hf->pBlock->dwFlags & MPQ_FILE_COMPRESSED)
        FREEMEM(tempBuffer);

    return dwBytesRead;
}