static int CopyMpqFiles(TMPQArchive * ha, LPDWORD pFileKeys, TFileStream * pNewStream)
{
    TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
    TFileEntry * pFileEntry;
    TMPQFile * hf = NULL;
    int nError = ERROR_SUCCESS;

    // Walk through all files and write them to the destination MPQ archive
    for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
    {
        // Copy all the file sectors
        // Only do that when the file has nonzero size
        if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && pFileEntry->dwFileSize != 0)
        {
            // Allocate structure for the MPQ file
            hf = CreateMpqFile(ha);
            if(hf == NULL)
                return ERROR_NOT_ENOUGH_MEMORY;

            // Store file entry
            hf->pFileEntry = pFileEntry;

            // Set the raw file position
            hf->MpqFilePos = pFileEntry->ByteOffset;
            hf->RawFilePos = ha->MpqPos + hf->MpqFilePos;

            // Set the file decryption key
            hf->dwFileKey = pFileKeys[pFileEntry - ha->pFileTable];
            hf->dwDataSize = pFileEntry->dwFileSize;

            // If the file is a patch file, load the patch header
            if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE)
            {
                nError = AllocatePatchInfo(hf, true);
                if(nError != ERROR_SUCCESS)
                    break;
            }

            // Allocate buffers for file sector and sector offset table
            nError = AllocateSectorBuffer(hf);
            if(nError != ERROR_SUCCESS)
                break;

            // Also allocate sector offset table and sector checksum table
            nError = AllocateSectorOffsets(hf, true);
            if(nError != ERROR_SUCCESS)
                break;

            // Also load sector checksums, if any
            if(pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC)
            {
                nError = AllocateSectorChecksums(hf, false);
                if(nError != ERROR_SUCCESS)
                    break;
            }

            // Copy all file sectors
            nError = CopyMpqFileSectors(ha, hf, pNewStream);
            if(nError != ERROR_SUCCESS)
                break;

            // Free buffers. This also sets "hf" to NULL.
            FreeMPQFile(hf);
        }
    }

    // Cleanup and exit
    if(hf != NULL)
        FreeMPQFile(hf);
    return nError;
}
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;
}
int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD dwCompression)
{
    TMPQArchive * ha;
    TMPQBlock * pBlock;
    DWORD dwSectorPosLen = 0;
    int nError = ERROR_SUCCESS;

    // Don't bother if the caller gave us zero size
    if(pvData == NULL || dwSize == 0)
        return ERROR_SUCCESS;

    // Get pointer to the MPQ archive
    pBlock = hf->pBlock;
    ha = hf->ha;

    // Allocate file buffers
    if(hf->pbFileSector == NULL)
    {
        nError = AllocateSectorBuffer(hf);
        if(nError != ERROR_SUCCESS)
        {
            hf->bErrorOccured = true;
            return nError;
        }

        // Allocate sector offsets
        if(hf->SectorOffsets == NULL)
        {
            nError = AllocateSectorOffsets(hf, false);
            if(nError != ERROR_SUCCESS)
            {
                hf->bErrorOccured = true;
                return nError;
            }
        }

        // Create array of sector checksums
        if(hf->SectorChksums == NULL && (pBlock->dwFlags & MPQ_FILE_SECTOR_CRC))
        {
            nError = AllocateSectorChecksums(hf, false);
            if(nError != ERROR_SUCCESS)
            {
                hf->bErrorOccured = true;
                return nError;
            }
        }

        // Pre-save the sector offset table, just to reserve space in the file.
        // Note that we dont need to swap the sector positions, nor encrypt the table
        // at the moment, as it will be written again after writing all file sectors.
        if(hf->SectorOffsets != NULL)
        {
            dwSectorPosLen = hf->dwSectorCount * sizeof(DWORD);
            if(!FileStream_Write(ha->pStream, &hf->RawFilePos, hf->SectorOffsets, dwSectorPosLen))
                nError = GetLastError();

            pBlock->dwCSize += dwSectorPosLen;
        }
    }

    // Write the MPQ data to the file
    if(nError == ERROR_SUCCESS)
        nError = WriteDataToMpqFile(ha, hf, (LPBYTE)pvData, dwSize, dwCompression);

    // If it succeeded and we wrote all the file data,
    // we need to re-save sector offset table
    if((nError == ERROR_SUCCESS) && (hf->dwFilePos >= pBlock->dwFSize))
    {
        // Finish calculating CRC32
        if(hf->pCrc32 != NULL)
            *hf->pCrc32 = hf->dwCrc32;

        // Finish calculating MD5
        if(hf->pMd5 != NULL)
            tommd5_done((hash_state *)hf->hctx, hf->pMd5->Value);

        // If we also have sector checksums, write them to the file
        if(hf->SectorChksums != NULL)
            WriteSectorChecksums(hf);

        // Now write sector offsets to the file
        if(hf->SectorOffsets != NULL)
            WriteSectorOffsets(hf);
    }

    if(nError != ERROR_SUCCESS)
        hf->bErrorOccured = true;
    return nError;
}
Beispiel #4
0
static int RecryptFileData(
    TMPQArchive * ha,
    TMPQFile * hf,
    const char * szFileName,
    const char * szNewFileName)
{
    ULONGLONG RawFilePos;
    TFileEntry * pFileEntry = hf->pFileEntry;
    DWORD dwBytesToRecrypt = pFileEntry->dwCmpSize;
    DWORD dwOldKey;
    DWORD dwNewKey;
    int nError = ERROR_SUCCESS;

    // The file must be encrypted
    assert(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED);

    // File decryption key is calculated from the plain name
    szNewFileName = GetPlainFileNameA(szNewFileName);
    szFileName = GetPlainFileNameA(szFileName);

    // Calculate both file keys
    dwOldKey = DecryptFileKey(szFileName,    pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags);
    dwNewKey = DecryptFileKey(szNewFileName, pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags);

    // Incase the keys are equal, don't recrypt the file
    if(dwNewKey == dwOldKey)
        return ERROR_SUCCESS;
    hf->dwFileKey = dwOldKey;

    // Calculate the raw position of the file in the archive
    hf->MpqFilePos = pFileEntry->ByteOffset;
    hf->RawFilePos = ha->MpqPos + hf->MpqFilePos;

    // Allocate buffer for file transfer
    nError = AllocateSectorBuffer(hf);
    if(nError != ERROR_SUCCESS)
        return nError;

    // Also allocate buffer for sector offsets
    // Note: Don't load sector checksums, we don't need to recrypt them
    nError = AllocateSectorOffsets(hf, true);
    if(nError != ERROR_SUCCESS)
        return nError;

    // If we have sector offsets, recrypt these as well
    if(hf->SectorOffsets != NULL)
    {
        // Allocate secondary buffer for sectors copy
        DWORD * SectorOffsetsCopy = (DWORD *)STORM_ALLOC(BYTE, hf->SectorOffsets[0]);
        DWORD dwSectorOffsLen = hf->SectorOffsets[0];

        if(SectorOffsetsCopy == NULL)
            return ERROR_NOT_ENOUGH_MEMORY;

        // Recrypt the array of sector offsets
        memcpy(SectorOffsetsCopy, hf->SectorOffsets, dwSectorOffsLen);
        EncryptMpqBlock(SectorOffsetsCopy, dwSectorOffsLen, dwNewKey - 1);
        BSWAP_ARRAY32_UNSIGNED(SectorOffsetsCopy, dwSectorOffsLen);

        // Write the recrypted array back
        if(!FileStream_Write(ha->pStream, &hf->RawFilePos, SectorOffsetsCopy, dwSectorOffsLen))
            nError = GetLastError();
        STORM_FREE(SectorOffsetsCopy);
    }

    // Now we have to recrypt all file sectors. We do it without
    // recompression, because recompression is not necessary in this case
    if(nError == ERROR_SUCCESS)
    {
        for(DWORD dwSector = 0; dwSector < hf->dwSectorCount; dwSector++)
        {
            DWORD dwRawDataInSector = hf->dwSectorSize;
            DWORD dwRawByteOffset = dwSector * hf->dwSectorSize;

            // Last sector: If there is not enough bytes remaining in the file, cut the raw size
            if(dwRawDataInSector > dwBytesToRecrypt)
                dwRawDataInSector = dwBytesToRecrypt;

            // Fix the raw data length if the file is compressed
            if(hf->SectorOffsets != NULL)
            {
                dwRawDataInSector = hf->SectorOffsets[dwSector+1] - hf->SectorOffsets[dwSector];
                dwRawByteOffset = hf->SectorOffsets[dwSector];
            }

            // Calculate the raw file offset of the file sector
            CalculateRawSectorOffset(RawFilePos, hf, dwRawByteOffset);

            // Read the file sector
            if(!FileStream_Read(ha->pStream, &RawFilePos, hf->pbFileSector, dwRawDataInSector))
            {
                nError = GetLastError();
                break;
            }

            // If necessary, re-encrypt the sector
            // Note: Recompression is not necessary here. Unlike encryption, 
            // the compression does not depend on the position of the file in MPQ.
            BSWAP_ARRAY32_UNSIGNED(hf->pbFileSector, dwRawDataInSector);
            DecryptMpqBlock(hf->pbFileSector, dwRawDataInSector, dwOldKey + dwSector);
            EncryptMpqBlock(hf->pbFileSector, dwRawDataInSector, dwNewKey + dwSector);
            BSWAP_ARRAY32_UNSIGNED(hf->pbFileSector, dwRawDataInSector);

            // Write the sector back
            if(!FileStream_Write(ha->pStream, &RawFilePos, hf->pbFileSector, dwRawDataInSector))
            {
                nError = GetLastError();
                break;
            }

            // Decrement number of bytes remaining
            dwBytesToRecrypt -= hf->dwSectorSize;
        }
    }

    return nError;
}
int SFileAddFile_Write(TMPQFile * hf, const void * pvData, DWORD dwSize, DWORD dwCompression)
{
    TMPQArchive * ha;
    TFileEntry * pFileEntry;
    int nError = ERROR_SUCCESS;

    // Don't bother if the caller gave us zero size
    if(pvData == NULL || dwSize == 0)
        return ERROR_SUCCESS;

    // Get pointer to the MPQ archive
    pFileEntry = hf->pFileEntry;
    ha = hf->ha;

    // Allocate file buffers
    if(hf->pbFileSector == NULL)
    {
        ULONGLONG RawFilePos = hf->RawFilePos;

        // Allocate buffer for file sector
        hf->nAddFileError = nError = AllocateSectorBuffer(hf);
        if(nError != ERROR_SUCCESS)
            return nError;

        // Allocate patch info, if the data is patch
        if(hf->pPatchInfo == NULL && IsIncrementalPatchFile(pvData, dwSize, &hf->dwPatchedFileSize))
        {
            // Set the MPQ_FILE_PATCH_FILE flag
            hf->pFileEntry->dwFlags |= MPQ_FILE_PATCH_FILE;

            // Allocate the patch info
            hf->nAddFileError = nError = AllocatePatchInfo(hf, false);
            if(nError != ERROR_SUCCESS)
                return nError;
        }

        // Allocate sector offsets
        if(hf->SectorOffsets == NULL)
        {
            hf->nAddFileError = nError = AllocateSectorOffsets(hf, false);
            if(nError != ERROR_SUCCESS)
                return nError;
        }

        // Create array of sector checksums
        if(hf->SectorChksums == NULL && (pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC))
        {
            hf->nAddFileError = nError = AllocateSectorChecksums(hf, false);
            if(nError != ERROR_SUCCESS)
                return nError;
        }

        // Pre-save the patch info, if any
        if(hf->pPatchInfo != NULL)
        {
            if(!FileStream_Write(ha->pStream, &RawFilePos, hf->pPatchInfo, hf->pPatchInfo->dwLength))
                nError = GetLastError();
  
            pFileEntry->dwCmpSize += hf->pPatchInfo->dwLength;
            RawFilePos += hf->pPatchInfo->dwLength;
        }

        // Pre-save the sector offset table, just to reserve space in the file.
        // Note that we dont need to swap the sector positions, nor encrypt the table
        // at the moment, as it will be written again after writing all file sectors.
        if(hf->SectorOffsets != NULL)
        {
            if(!FileStream_Write(ha->pStream, &RawFilePos, hf->SectorOffsets, hf->SectorOffsets[0]))
                nError = GetLastError();

            pFileEntry->dwCmpSize += hf->SectorOffsets[0];
            RawFilePos += hf->SectorOffsets[0];
        }
    }

    // Write the MPQ data to the file
    if(nError == ERROR_SUCCESS)
    {
        // Save the first sector compression to the file structure
        // Note that the entire first file sector will be compressed
        // by compression that was passed to the first call of SFileAddFile_Write
        if(hf->dwFilePos == 0)
            hf->dwCompression0 = dwCompression;

        // Write the data to the MPQ
        nError = WriteDataToMpqFile(ha, hf, (LPBYTE)pvData, dwSize, dwCompression);
    }

    // If it succeeded and we wrote all the file data,
    // we need to re-save sector offset table
    if(nError == ERROR_SUCCESS)
    {
        if(hf->dwFilePos >= pFileEntry->dwFileSize)
        {
            // Finish calculating CRC32
            hf->pFileEntry->dwCrc32 = hf->dwCrc32;

            // Finish calculating MD5
            md5_done((hash_state *)hf->hctx, hf->pFileEntry->md5);

            // If we also have sector checksums, write them to the file
            if(hf->SectorChksums != NULL)
            {
                nError = WriteSectorChecksums(hf);
            }

            // Now write patch info
            if(hf->pPatchInfo != NULL)
            {
                memcpy(hf->pPatchInfo->md5, hf->pFileEntry->md5, MD5_DIGEST_SIZE);
                hf->pPatchInfo->dwDataSize  = hf->pFileEntry->dwFileSize;
                hf->pFileEntry->dwFileSize = hf->dwPatchedFileSize;
                nError = WritePatchInfo(hf);
            }

            // Now write sector offsets to the file
            if(hf->SectorOffsets != NULL)
            {
                nError = WriteSectorOffsets(hf);
            }

            // Write the MD5 hashes of each file chunk, if required
            if(ha->pHeader->dwRawChunkSize != 0)
            {
                nError = WriteMpqDataMD5(ha->pStream,
                                         ha->MpqPos + hf->pFileEntry->ByteOffset,
                                         hf->pFileEntry->dwCmpSize,
                                         ha->pHeader->dwRawChunkSize);
            }
        }
    }

    // Store the error code from the Write File operation
    hf->nAddFileError = nError;
    return nError;
}
static int ReadMpqFile(TMPQFile * hf, void * pvBuffer, DWORD dwFilePos, DWORD dwBytesToRead, LPDWORD pdwBytesRead)
{
    TMPQArchive * ha = hf->ha;
    LPBYTE pbBuffer = (BYTE *)pvBuffer;
    DWORD dwTotalBytesRead = 0;                         // Total bytes read in all three parts
    DWORD dwSectorSizeMask = ha->dwSectorSize - 1;      // Mask for block size, usually 0x0FFF
    DWORD dwFileSectorPos;                              // File offset of the loaded sector
    DWORD dwBytesRead;                                  // Number of bytes read (temporary variable)
    int nError;

    // If the file position is at or beyond end of file, do nothing
    if(dwFilePos >= hf->dwDataSize)
    {
        *pdwBytesRead = 0;
        return ERROR_SUCCESS;
    }

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

    // Compute sector position in the file
    dwFileSectorPos = dwFilePos & ~dwSectorSizeMask;  // Position in the block

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

    // Load the first (incomplete) file sector
    if(dwFilePos & dwSectorSizeMask)
    {
        DWORD dwBytesInSector = ha->dwSectorSize;
        DWORD dwBufferOffs = dwFilePos & dwSectorSizeMask;
        DWORD dwToCopy;

        // Is the file sector already loaded ?
        if(hf->dwSectorOffs != dwFileSectorPos)
        {
            // Load one MPQ sector into archive buffer
            nError = ReadMpqSectors(hf, hf->pbFileSector, dwFileSectorPos, ha->dwSectorSize, &dwBytesInSector);
            if(nError != ERROR_SUCCESS)
                return nError;

            // Remember that the data loaded to the sector have new file offset
            hf->dwSectorOffs = dwFileSectorPos;
        }
        else
        {
            if((dwFileSectorPos + dwBytesInSector) > hf->dwDataSize)
                dwBytesInSector = hf->dwDataSize - dwFileSectorPos;
        }

        // Copy the data from the offset in the loaded sector to the end of the sector
        dwToCopy = dwBytesInSector - dwBufferOffs;
        if(dwToCopy > dwBytesToRead)
            dwToCopy = dwBytesToRead;

        // Copy data from sector buffer into target buffer
        memcpy(pbBuffer, hf->pbFileSector + dwBufferOffs, dwToCopy);

        // Update pointers and byte counts
        dwTotalBytesRead += dwToCopy;
        dwFileSectorPos  += dwBytesInSector;
        pbBuffer         += dwToCopy;
        dwBytesToRead    -= dwToCopy;
    }

    // Load the whole ("middle") sectors only if there is at least one full sector to be read
    if(dwBytesToRead >= ha->dwSectorSize)
    {
        DWORD dwBlockBytes = dwBytesToRead & ~dwSectorSizeMask;

        // Load all sectors to the output buffer
        nError = ReadMpqSectors(hf, pbBuffer, dwFileSectorPos, dwBlockBytes, &dwBytesRead);
        if(nError != ERROR_SUCCESS)
            return nError;

        // Update pointers
        dwTotalBytesRead += dwBytesRead;
        dwFileSectorPos  += dwBytesRead;
        pbBuffer         += dwBytesRead;
        dwBytesToRead    -= dwBytesRead;
    }

    // Read the terminating sector
    if(dwBytesToRead > 0)
    {
        DWORD dwToCopy = ha->dwSectorSize;

        // Is the file sector already loaded ?
        if(hf->dwSectorOffs != dwFileSectorPos)
        {
            // Load one MPQ sector into archive buffer
            nError = ReadMpqSectors(hf, hf->pbFileSector, dwFileSectorPos, ha->dwSectorSize, &dwBytesRead);
            if(nError != ERROR_SUCCESS)
                return nError;

            // Remember that the data loaded to the sector have new file offset
            hf->dwSectorOffs = dwFileSectorPos;
        }

        // Check number of bytes read
        if(dwToCopy > dwBytesToRead)
            dwToCopy = dwBytesToRead;

        // Copy the data from the cached last sector to the caller's buffer
        memcpy(pbBuffer, hf->pbFileSector, dwToCopy);

        // Update pointers
        dwTotalBytesRead += dwToCopy;
    }

    // Store total number of bytes read to the caller
    *pdwBytesRead = dwTotalBytesRead;
    return ERROR_SUCCESS;
}
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;
    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_COMPRESSED)
        {
            // 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_COMPRESSED)
        {
            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)
            {
                if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_2)
                    nResult = SCompDecompress2((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, cbInBuffer);
                else
                    nResult = SCompDecompress((char *)hf->pbFileSector, &cbOutBuffer, (char *)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((char *)hf->pbFileSector, &cbOutBuffer, (char *)pbRawData, cbInBuffer);

            nError = (nResult != 0) ? ERROR_SUCCESS : 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;
}
Beispiel #8
0
static int CopyMpqFiles(TMPQArchive * ha, LPDWORD pFileKeys, TFileStream * pNewStream)
{
    TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
    TFileEntry * pFileEntry;
    TMPQFile * hf = NULL;
    ULONGLONG MpqFilePos;
    int nError = ERROR_SUCCESS;

    // Walk through all files and write them to the destination MPQ archive
    for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
    {
        // Copy all the file sectors
        // Only do that when the file has nonzero size
        if((pFileEntry->dwFlags & MPQ_FILE_EXISTS))
        {
            // Query the position where the destination file will be
            FileStream_GetPos(pNewStream, &MpqFilePos);
            MpqFilePos = MpqFilePos - ha->MpqPos;

            // Perform file copy ONLY if the file has nonzero size
            if(pFileEntry->dwFileSize != 0)
            {
                // Allocate structure for the MPQ file
                hf = CreateFileHandle(ha, pFileEntry);
                if(hf == NULL)
                    return ERROR_NOT_ENOUGH_MEMORY;

                // Set the file decryption key
                hf->dwFileKey = pFileKeys[pFileEntry - ha->pFileTable];

                // If the file is a patch file, load the patch header
                if(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE)
                {
                    nError = AllocatePatchInfo(hf, true);
                    if(nError != ERROR_SUCCESS)
                        break;
                }

                // Allocate buffers for file sector and sector offset table
                nError = AllocateSectorBuffer(hf);
                if(nError != ERROR_SUCCESS)
                    break;

                // Also allocate sector offset table and sector checksum table
                nError = AllocateSectorOffsets(hf, true);
                if(nError != ERROR_SUCCESS)
                    break;

                // Also load sector checksums, if any
                if(pFileEntry->dwFlags & MPQ_FILE_SECTOR_CRC)
                {
                    nError = AllocateSectorChecksums(hf, false);
                    if(nError != ERROR_SUCCESS)
                        break;
                }

                // Copy all file sectors
                nError = CopyMpqFileSectors(ha, hf, pNewStream, MpqFilePos);
                if(nError != ERROR_SUCCESS)
                    break;

                // Free buffers. This also sets "hf" to NULL.
                FreeFileHandle(hf);
            }

            // Note: DO NOT update the compressed size in the file entry, no matter how bad it is.
            pFileEntry->ByteOffset = MpqFilePos;
        }
    }

    // Cleanup and exit
    if(hf != NULL)
        FreeFileHandle(hf);
    return nError;
}
static int CopyMpqFiles(TMPQArchive * ha, DWORD * pFileKeys, TFileStream * pNewStream)
{
    TMPQBlockEx * pBlockEx = ha->pExtBlockTable;
    TMPQBlock * pBlockEnd = ha->pBlockTable + ha->pHeader->dwBlockTableSize;
    TMPQBlock * pBlock;
    TMPQFile * hf = NULL;
    int nError = ERROR_SUCCESS;

    // Walk through all files and write them to the destination MPQ archive
    for(pBlock = ha->pBlockTable; pBlock < pBlockEnd; pBlock++, pBlockEx++)
    {
        // Copy all the file sectors
        // Only do that when the file has nonzero size
        if((pBlock->dwFlags & MPQ_FILE_EXISTS) && pBlock->dwFSize != 0)
        {
            // Allocate structure for the MPQ file
            hf = CreateMpqFile(ha, "<compacting>");
            if(hf == NULL)
                return ERROR_NOT_ENOUGH_MEMORY;

            // Store block positions
            hf->pBlockEx = pBlockEx;
            hf->pBlock = pBlock;

            // Set the raw file position
            hf->MpqFilePos.HighPart = hf->pBlockEx->wFilePosHigh;
            hf->MpqFilePos.LowPart  = hf->pBlock->dwFilePos;
            hf->RawFilePos.QuadPart = ha->MpqPos.QuadPart + hf->MpqFilePos.LowPart;

            // Set the file decryption key
            hf->dwFileKey = pFileKeys[pBlock - ha->pBlockTable];

            // Allocate buffers for file sector and sector offset table
            nError = AllocateSectorBuffer(hf);
            if(nError != ERROR_SUCCESS)
                break;

            // Also allocate sector offset table and sector checksum table
            nError = AllocateSectorOffsets(hf, true);
            if(nError != ERROR_SUCCESS)
                break;

            if(pBlock->dwFlags & MPQ_FILE_SECTOR_CRC)
            {
                nError = AllocateSectorChecksums(hf, false);
                if(nError != ERROR_SUCCESS)
                    break;
            }

            // Copy all file sectors
            nError = CopyMpqFileSectors(ha, hf, pNewStream);
            if(nError != ERROR_SUCCESS)
                break;

            // Free buffers. This also sets "hf" to NULL.
            FreeMPQFile(hf);
        }
    }

    // Cleanup and exit
    if(hf != NULL)
        FreeMPQFile(hf);
    return nError;
}