diff options
Diffstat (limited to 'dep/StormLib/src/SFilePatchArchives.cpp')
-rw-r--r-- | dep/StormLib/src/SFilePatchArchives.cpp | 587 |
1 files changed, 0 insertions, 587 deletions
diff --git a/dep/StormLib/src/SFilePatchArchives.cpp b/dep/StormLib/src/SFilePatchArchives.cpp deleted file mode 100644 index 24ae2c52c37..00000000000 --- a/dep/StormLib/src/SFilePatchArchives.cpp +++ /dev/null @@ -1,587 +0,0 @@ -/*****************************************************************************/ -/* SFilePatchArchives.cpp Copyright (c) Ladislav Zezula 2010 */ -/*---------------------------------------------------------------------------*/ -/* Description: */ -/*---------------------------------------------------------------------------*/ -/* Date Ver Who Comment */ -/* -------- ---- --- ------- */ -/* 18.08.10 1.00 Lad The first version of SFilePatchArchives.cpp */ -/*****************************************************************************/ - -#define __STORMLIB_SELF__ -#include "StormLib.h" -#include "StormCommon.h" - -//----------------------------------------------------------------------------- -// Local structures - -typedef struct _BLIZZARD_BSDIFF40_FILE -{ - ULONGLONG Signature; - ULONGLONG CtrlBlockSize; - ULONGLONG DataBlockSize; - ULONGLONG NewFileSize; -} BLIZZARD_BSDIFF40_FILE, *PBLIZZARD_BSDIFF40_FILE; - -//----------------------------------------------------------------------------- -// Local functions - -static bool GetDefaultPatchPrefix( - const TCHAR * szBaseMpqName, - char * szBuffer) -{ - const TCHAR * szExtension; - const TCHAR * szDash; - - // Ensure that both names are plain names - szBaseMpqName = GetPlainFileNameT(szBaseMpqName); - - // Patch prefix is for the Cataclysm MPQs, whose names - // are like "locale-enGB.MPQ" or "speech-enGB.MPQ" - szExtension = _tcsrchr(szBaseMpqName, _T('.')); - szDash = _tcsrchr(szBaseMpqName, _T('-')); - strcpy(szBuffer, "Base"); - - // If the length of the prefix doesn't match, use default one - if(szExtension != NULL && szDash != NULL && (szExtension - szDash) == 5) - { - // Copy the prefix - szBuffer[0] = (char)szDash[1]; - szBuffer[1] = (char)szDash[2]; - szBuffer[2] = (char)szDash[3]; - szBuffer[3] = (char)szDash[4]; - szBuffer[4] = 0; - } - - return true; -} - -static void Decompress_RLE(LPBYTE pbDecompressed, DWORD cbDecompressed, LPBYTE pbCompressed, DWORD cbCompressed) -{ - LPBYTE pbDecompressedEnd = pbDecompressed + cbDecompressed; - LPBYTE pbCompressedEnd = pbCompressed + cbCompressed; - BYTE RepeatCount; - BYTE OneByte; - - // Cut the initial DWORD from the compressed chunk - pbCompressed += sizeof(DWORD); - cbCompressed -= sizeof(DWORD); - - // Pre-fill decompressed buffer with zeros - memset(pbDecompressed, 0, cbDecompressed); - - // Unpack - while(pbCompressed < pbCompressedEnd && pbDecompressed < pbDecompressedEnd) - { - OneByte = *pbCompressed++; - - // Is it a repetition byte ? - if(OneByte & 0x80) - { - RepeatCount = (OneByte & 0x7F) + 1; - for(BYTE i = 0; i < RepeatCount; i++) - { - if(pbDecompressed == pbDecompressedEnd || pbCompressed == pbCompressedEnd) - break; - - *pbDecompressed++ = *pbCompressed++; - } - } - else - { - pbDecompressed += (OneByte + 1); - } - } -} - -static int LoadMpqPatch_COPY(TMPQFile * hf, TPatchHeader * pPatchHeader) -{ - int nError = ERROR_SUCCESS; - - // Allocate space for patch header and compressed data - hf->pPatchHeader = (TPatchHeader *)STORM_ALLOC(BYTE, pPatchHeader->dwSizeOfPatchData); - if(hf->pPatchHeader == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; - - // Load the patch data and decide if they are compressed or not - if(nError == ERROR_SUCCESS) - { - LPBYTE pbPatchFile = (LPBYTE)hf->pPatchHeader; - - // Copy the patch header itself - memcpy(pbPatchFile, pPatchHeader, sizeof(TPatchHeader)); - pbPatchFile += sizeof(TPatchHeader); - - // Load the rest of the patch - if(!SFileReadFile((HANDLE)hf, pbPatchFile, pPatchHeader->dwSizeOfPatchData - sizeof(TPatchHeader))) - nError = GetLastError(); - } - - return nError; -} - -static int LoadMpqPatch_BSD0(TMPQFile * hf, TPatchHeader * pPatchHeader) -{ - LPBYTE pbDecompressed = NULL; - LPBYTE pbCompressed = NULL; - DWORD cbDecompressed = 0; - DWORD cbCompressed = 0; - DWORD dwBytesRead = 0; - int nError = ERROR_SUCCESS; - - // Allocate space for compressed data - cbCompressed = pPatchHeader->dwXfrmBlockSize - SIZE_OF_XFRM_HEADER; - pbCompressed = STORM_ALLOC(BYTE, cbCompressed); - if(pbCompressed == NULL) - nError = ERROR_SUCCESS; - - // Read the compressed patch data - if(nError == ERROR_SUCCESS) - { - // Load the rest of the header - SFileReadFile((HANDLE)hf, pbCompressed, cbCompressed, &dwBytesRead); - if(dwBytesRead != cbCompressed) - nError = ERROR_FILE_CORRUPT; - } - - // Get the uncompressed size of the patch - if(nError == ERROR_SUCCESS) - { - cbDecompressed = pPatchHeader->dwSizeOfPatchData - sizeof(TPatchHeader); - hf->pPatchHeader = (TPatchHeader *)STORM_ALLOC(BYTE, pPatchHeader->dwSizeOfPatchData); - if(hf->pPatchHeader == NULL) - nError = ERROR_NOT_ENOUGH_MEMORY; - } - - // Now decompress the patch data - if(nError == ERROR_SUCCESS) - { - // Copy the patch header - memcpy(hf->pPatchHeader, pPatchHeader, sizeof(TPatchHeader)); - pbDecompressed = (LPBYTE)hf->pPatchHeader + sizeof(TPatchHeader); - - // Uncompress or copy the patch data - if(cbCompressed < cbDecompressed) - { - Decompress_RLE(pbDecompressed, cbDecompressed, pbCompressed, cbCompressed); - } - else - { - assert(cbCompressed == cbDecompressed); - memcpy(pbDecompressed, pbCompressed, cbCompressed); - } - } - - // Free buffers and exit - if(pbCompressed != NULL) - STORM_FREE(pbCompressed); - return nError; -} - -static int ApplyMpqPatch_COPY( - TMPQFile * hf, - TPatchHeader * pPatchHeader) -{ - LPBYTE pbNewFileData; - DWORD cbNewFileData; - - // Allocate space for new file data - cbNewFileData = pPatchHeader->dwXfrmBlockSize - SIZE_OF_XFRM_HEADER; - pbNewFileData = STORM_ALLOC(BYTE, cbNewFileData); - if(pbNewFileData == NULL) - return ERROR_NOT_ENOUGH_MEMORY; - - // Copy the patch data as-is - memcpy(pbNewFileData, (LPBYTE)pPatchHeader + sizeof(TPatchHeader), cbNewFileData); - - // Free the old file data - STORM_FREE(hf->pbFileData); - - // Put the new file data there - hf->pbFileData = pbNewFileData; - hf->cbFileData = cbNewFileData; - return ERROR_SUCCESS; -} - -static int ApplyMpqPatch_BSD0( - TMPQFile * hf, - TPatchHeader * pPatchHeader) -{ - PBLIZZARD_BSDIFF40_FILE pBsdiff; - LPDWORD pCtrlBlock; - LPBYTE pbPatchData = (LPBYTE)pPatchHeader + sizeof(TPatchHeader); - LPBYTE pDataBlock; - LPBYTE pExtraBlock; - LPBYTE pbNewData = NULL; - LPBYTE pbOldData = (LPBYTE)hf->pbFileData; - DWORD dwNewOffset = 0; // Current position to patch - DWORD dwOldOffset = 0; // Current source position - DWORD dwNewSize; // Patched file size - DWORD dwOldSize = hf->cbFileData; // File size before patch - - // Get pointer to the patch header - // Format of BSDIFF header corresponds to original BSDIFF, which is: - // 0000 8 bytes signature "BSDIFF40" - // 0008 8 bytes size of the control block - // 0010 8 bytes size of the data block - // 0018 8 bytes new size of the patched file - pBsdiff = (PBLIZZARD_BSDIFF40_FILE)pbPatchData; - pbPatchData += sizeof(BLIZZARD_BSDIFF40_FILE); - - // Get pointer to the 32-bit BSDIFF control block - // The control block follows immediately after the BSDIFF header - // and consists of three 32-bit integers - // 0000 4 bytes Length to copy from the BSDIFF data block the new file - // 0004 4 bytes Length to copy from the BSDIFF extra block - // 0008 4 bytes Size to increment source file offset - pCtrlBlock = (LPDWORD)pbPatchData; - pbPatchData += (size_t)BSWAP_INT64_UNSIGNED(pBsdiff->CtrlBlockSize); - - // Get the pointer to the data block - pDataBlock = (LPBYTE)pbPatchData; - pbPatchData += (size_t)BSWAP_INT64_UNSIGNED(pBsdiff->DataBlockSize); - - // Get the pointer to the extra block - pExtraBlock = (LPBYTE)pbPatchData; - dwNewSize = (DWORD)BSWAP_INT64_UNSIGNED(pBsdiff->NewFileSize); - - // Allocate new buffer - pbNewData = STORM_ALLOC(BYTE, dwNewSize); - if(pbNewData == NULL) - return ERROR_NOT_ENOUGH_MEMORY; - - // Now patch the file - while(dwNewOffset < dwNewSize) - { - DWORD dwAddDataLength = BSWAP_INT32_UNSIGNED(pCtrlBlock[0]); - DWORD dwMovDataLength = BSWAP_INT32_UNSIGNED(pCtrlBlock[1]); - DWORD dwOldMoveLength = BSWAP_INT32_UNSIGNED(pCtrlBlock[2]); - DWORD i; - - // Sanity check - if((dwNewOffset + dwAddDataLength) > dwNewSize) - { - STORM_FREE(pbNewData); - return ERROR_FILE_CORRUPT; - } - - // Read the diff string to the target buffer - memcpy(pbNewData + dwNewOffset, pDataBlock, dwAddDataLength); - pDataBlock += dwAddDataLength; - - // Now combine the patch data with the original file - for(i = 0; i < dwAddDataLength; i++) - { - if(dwOldOffset < dwOldSize) - pbNewData[dwNewOffset] = pbNewData[dwNewOffset] + pbOldData[dwOldOffset]; - - dwNewOffset++; - dwOldOffset++; - } - - // Sanity check - if((dwNewOffset + dwMovDataLength) > dwNewSize) - { - STORM_FREE(pbNewData); - return ERROR_FILE_CORRUPT; - } - - // Copy the data from the extra block in BSDIFF patch - memcpy(pbNewData + dwNewOffset, pExtraBlock, dwMovDataLength); - pExtraBlock += dwMovDataLength; - dwNewOffset += dwMovDataLength; - - // Move the old offset - if(dwOldMoveLength & 0x80000000) - dwOldMoveLength = 0x80000000 - dwOldMoveLength; - dwOldOffset += dwOldMoveLength; - pCtrlBlock += 3; - } - - // Free the old file data - STORM_FREE(hf->pbFileData); - - // Put the new data to the fil structure - hf->pbFileData = pbNewData; - hf->cbFileData = dwNewSize; - return ERROR_SUCCESS; -} - - -static int LoadMpqPatch(TMPQFile * hf) -{ - TPatchHeader PatchHeader; - DWORD dwBytesRead; - int nError = ERROR_SUCCESS; - - // Read the patch header - SFileReadFile((HANDLE)hf, &PatchHeader, sizeof(TPatchHeader), &dwBytesRead); - if(dwBytesRead != sizeof(TPatchHeader)) - nError = ERROR_FILE_CORRUPT; - - // Verify the signatures in the patch header - if(nError == ERROR_SUCCESS) - { - // BSWAP the entire header, if needed - BSWAP_ARRAY32_UNSIGNED(&PatchHeader, sizeof(DWORD) * 6); - PatchHeader.dwXFRM = BSWAP_INT32_UNSIGNED(PatchHeader.dwXFRM); - PatchHeader.dwXfrmBlockSize = BSWAP_INT32_UNSIGNED(PatchHeader.dwXfrmBlockSize); - PatchHeader.dwPatchType = BSWAP_INT32_UNSIGNED(PatchHeader.dwPatchType); - - if(PatchHeader.dwSignature != 0x48435450 || PatchHeader.dwMD5 != 0x5f35444d || PatchHeader.dwXFRM != 0x4d524658) - nError = ERROR_FILE_CORRUPT; - } - - // Read the patch, depending on patch type - if(nError == ERROR_SUCCESS) - { - switch(PatchHeader.dwPatchType) - { - case 0x59504f43: // 'COPY' - nError = LoadMpqPatch_COPY(hf, &PatchHeader); - break; - - case 0x30445342: // 'BSD0' - nError = LoadMpqPatch_BSD0(hf, &PatchHeader); - break; - - default: - nError = ERROR_FILE_CORRUPT; - break; - } - } - - return nError; -} - -static int ApplyMpqPatch( - TMPQFile * hf, - TPatchHeader * pPatchHeader) -{ - int nError = ERROR_SUCCESS; - - // Verify the original file before patching - if(pPatchHeader->dwSizeBeforePatch != 0) - { - if(!VerifyDataBlockHash(hf->pbFileData, hf->cbFileData, pPatchHeader->md5_before_patch)) - nError = ERROR_FILE_CORRUPT; - } - - // Apply the patch - if(nError == ERROR_SUCCESS) - { - switch(pPatchHeader->dwPatchType) - { - case 0x59504f43: // 'COPY' - nError = ApplyMpqPatch_COPY(hf, pPatchHeader); - break; - - case 0x30445342: // 'BSD0' - nError = ApplyMpqPatch_BSD0(hf, pPatchHeader); - break; - - default: - nError = ERROR_FILE_CORRUPT; - break; - } - } - - // Verify MD5 after patch - if(nError == ERROR_SUCCESS && pPatchHeader->dwSizeAfterPatch != 0) - { - // Verify the patched file - if(!VerifyDataBlockHash(hf->pbFileData, hf->cbFileData, pPatchHeader->md5_after_patch)) - nError = ERROR_FILE_CORRUPT; - } - - return nError; -} - -//----------------------------------------------------------------------------- -// Public functions (StormLib internals) - -bool IsIncrementalPatchFile(const void * pvData, DWORD cbData, LPDWORD pdwPatchedFileSize) -{ - TPatchHeader * pPatchHeader = (TPatchHeader *)pvData; - BLIZZARD_BSDIFF40_FILE DiffFile; - DWORD dwPatchType; - - if(cbData >= sizeof(TPatchHeader) + sizeof(BLIZZARD_BSDIFF40_FILE)) - { - dwPatchType = BSWAP_INT32_UNSIGNED(pPatchHeader->dwPatchType); - if(dwPatchType == 0x30445342) - { - // Give the caller the patch file size - if(pdwPatchedFileSize != NULL) - { - Decompress_RLE((LPBYTE)&DiffFile, sizeof(BLIZZARD_BSDIFF40_FILE), (LPBYTE)(pPatchHeader + 1), sizeof(BLIZZARD_BSDIFF40_FILE)); - DiffFile.NewFileSize = BSWAP_INT64_UNSIGNED(DiffFile.NewFileSize); - *pdwPatchedFileSize = (DWORD)DiffFile.NewFileSize; - return true; - } - } - } - - return false; -} - -int PatchFileData(TMPQFile * hf) -{ - TMPQFile * hfBase = hf; - int nError = ERROR_SUCCESS; - - // Move to the first patch - hf = hf->hfPatchFile; - - // Now go through all patches and patch the original data - while(hf != NULL) - { - // This must be true - assert(hf->pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE); - - // Make sure that the patch data is loaded - nError = LoadMpqPatch(hf); - if(nError != ERROR_SUCCESS) - break; - - // Apply the patch - nError = ApplyMpqPatch(hfBase, hf->pPatchHeader); - if(nError != ERROR_SUCCESS) - break; - - // Move to the next patch - hf = hf->hfPatchFile; - } - - return nError; -} - -//----------------------------------------------------------------------------- -// Public functions - -// -// Patch prefix is the path subdirectory where the patched files are within MPQ. -// -// Example 1: -// Main MPQ: locale-enGB.MPQ -// Patch MPQ: wow-update-12694.MPQ -// File in main MPQ: DBFilesClient\Achievement.dbc -// File in patch MPQ: enGB\DBFilesClient\Achievement.dbc -// Path prefix: enGB -// -// Example 2: -// Main MPQ: expansion1.MPQ -// Patch MPQ: wow-update-12694.MPQ -// File in main MPQ: DBFilesClient\Achievement.dbc -// File in patch MPQ: Base\DBFilesClient\Achievement.dbc -// Path prefix: Base -// - -bool WINAPI SFileOpenPatchArchive( - HANDLE hMpq, - const TCHAR * szPatchMpqName, - const char * szPatchPathPrefix, - DWORD dwFlags) -{ - TMPQArchive * haPatch; - TMPQArchive * ha = (TMPQArchive *)hMpq; - HANDLE hPatchMpq = NULL; - char szPatchPrefixBuff[MPQ_PATCH_PREFIX_LEN]; - int nError = ERROR_SUCCESS; - - // Keep compiler happy - dwFlags = dwFlags; - - // Verify input parameters - if(!IsValidMpqHandle(ha)) - nError = ERROR_INVALID_HANDLE; - if(szPatchMpqName == NULL || *szPatchMpqName == 0) - nError = ERROR_INVALID_PARAMETER; - - // If the user didn't give the patch prefix, get default one - if(szPatchPathPrefix != NULL) - { - // Save length of the patch prefix - if(strlen(szPatchPathPrefix) > MPQ_PATCH_PREFIX_LEN - 2) - nError = ERROR_INVALID_PARAMETER; - } - - // - // We don't allow adding patches to archives that have been open for write - // - // Error scenario: - // - // 1) Open archive for writing - // 2) Modify or replace a file - // 3) Add patch archive to the opened MPQ - // 4) Read patched file - // 5) Now what ? - // - - if(nError == ERROR_SUCCESS) - { - if(!FileStream_IsReadOnly(ha->pStream)) - nError = ERROR_ACCESS_DENIED; - } - - // Open the archive like it is normal archive - if(nError == ERROR_SUCCESS) - { - if(!SFileOpenArchive(szPatchMpqName, 0, MPQ_OPEN_READ_ONLY, &hPatchMpq)) - return false; - haPatch = (TMPQArchive *)hPatchMpq; - - // Older WoW patches (build 13914) used to have - // several language versions in one patch file - // Those patches needed to have a path prefix - // We can distinguish such patches by not having the (patch_metadata) file - if(szPatchPathPrefix == NULL) - { - if(!SFileHasFile(hPatchMpq, PATCH_METADATA_NAME)) - { - GetDefaultPatchPrefix(FileStream_GetFileName(ha->pStream), szPatchPrefixBuff); - szPatchPathPrefix = szPatchPrefixBuff; - } - } - - // Save the prefix for patch file names. - // Make sure that there is backslash after it - if(szPatchPathPrefix != NULL && *szPatchPathPrefix != 0) - { - strcpy(haPatch->szPatchPrefix, szPatchPathPrefix); - strcat(haPatch->szPatchPrefix, "\\"); - haPatch->cchPatchPrefix = strlen(haPatch->szPatchPrefix); - } - - // Now add the patch archive to the list of patches to the original MPQ - while(ha != NULL) - { - if(ha->haPatch == NULL) - { - haPatch->haBase = ha; - ha->haPatch = haPatch; - return true; - } - - // Move to the next archive - ha = ha->haPatch; - } - - // Should never happen - nError = ERROR_CAN_NOT_COMPLETE; - } - - SetLastError(nError); - return false; -} - -bool WINAPI SFileIsPatchedArchive(HANDLE hMpq) -{ - TMPQArchive * ha = (TMPQArchive *)hMpq; - - // Verify input parameters - if(!IsValidMpqHandle(ha)) - return false; - - return (ha->haPatch != NULL); -} |