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