static bool GetFilePatchChain(TMPQFile * hf, void * pvFileInfo, DWORD cbFileInfo, LPDWORD pcbLengthNeeded) { TMPQFile * hfTemp; TCHAR * szPatchChain = (TCHAR *)pvFileInfo; TCHAR * szFileName; size_t cchCharsNeeded = 1; size_t nLength; DWORD cbLengthNeeded; // Check if the "hf" is a MPQ file if(hf->pStream != NULL) { // Calculate the length needed szFileName = FileStream_GetFileName(hf->pStream); cchCharsNeeded += _tcslen(szFileName) + 1; cbLengthNeeded = (DWORD)(cchCharsNeeded * sizeof(TCHAR)); // If we have enough space, copy the file name if(cbFileInfo >= cbLengthNeeded) { nLength = _tcslen(szFileName) + 1; memcpy(szPatchChain, szFileName, nLength * sizeof(TCHAR)); szPatchChain += nLength; // Terminate the multi-string *szPatchChain = 0; } } else { // Calculate number of characters needed for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatchFile) cchCharsNeeded += _tcslen(FileStream_GetFileName(hfTemp->ha->pStream)) + 1; cbLengthNeeded = (DWORD)(cchCharsNeeded * sizeof(TCHAR)); // If we have enough space, the copy the patch chain if(cbFileInfo >= cbLengthNeeded) { for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatchFile) { szFileName = FileStream_GetFileName(hfTemp->ha->pStream); nLength = _tcslen(szFileName) + 1; memcpy(szPatchChain, szFileName, nLength * sizeof(TCHAR)); szPatchChain += nLength; } // Terminate the multi-string *szPatchChain = 0; } } // Give result length, terminate multi-string and return *pcbLengthNeeded = cbLengthNeeded; return true; }
static bool GetFilePatchChain(TMPQFile * hf, void * pvFileInfo, DWORD cbFileInfo, DWORD * pcbLengthNeeded) { TMPQFile * hfTemp; TCHAR * szFileInfo = (TCHAR *)pvFileInfo; size_t cchCharsNeeded = 1; size_t cchFileInfo = (cbFileInfo / sizeof(TCHAR)); size_t nLength; // Patch chain is only supported on MPQ files. if(hf->pStream != NULL) { SetLastError(ERROR_INVALID_PARAMETER); return false; } // Calculate the necessary length of the multi-string for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatch) cchCharsNeeded += _tcslen(FileStream_GetFileName(hfTemp->ha->pStream)) + 1; // Give the caller the needed length if(pcbLengthNeeded != NULL) pcbLengthNeeded[0] = (DWORD)(cchCharsNeeded * sizeof(TCHAR)); // If the caller gave both buffer pointer and data length, // try to copy the patch chain if(szFileInfo != NULL && cchFileInfo != 0) { // If there is enough space in the buffer, copy the patch chain if(cchCharsNeeded > cchFileInfo) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return false; } // Copy each patch for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatch) { // Get the file name and its length const TCHAR * szFileName = FileStream_GetFileName(hfTemp->ha->pStream); nLength = _tcslen(szFileName) + 1; // Copy the file name memcpy(szFileInfo, szFileName, nLength * sizeof(TCHAR)); szFileInfo += nLength; } // Make it multi-string szFileInfo[0] = 0; } return true; }
static int GetFilePatchChain(TMPQFile * hf, void * pvFileInfo, uint32_t cbFileInfo, uint32_t * pcbLengthNeeded) { TMPQFile * hfTemp; char * szFileInfo = (char *)pvFileInfo; size_t cchCharsNeeded = 1; size_t cchFileInfo = (cbFileInfo / sizeof(char)); size_t nLength; /* Patch chain is only supported on MPQ files. */ if(hf->pStream != NULL) { SetLastError(ERROR_INVALID_PARAMETER); return 0; } /* Calculate the necessary length of the multi-string */ for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatch) cchCharsNeeded += strlen(FileStream_GetFileName(hfTemp->ha->pStream)) + 1; /* Give the caller the needed length */ if(pcbLengthNeeded != NULL) pcbLengthNeeded[0] = (uint32_t)(cchCharsNeeded * sizeof(char)); /* If the caller gave both buffer pointer and data length, */ /* try to copy the patch chain */ if(szFileInfo != NULL && cchFileInfo != 0) { /* If there is enough space in the buffer, copy the patch chain */ if(cchCharsNeeded > cchFileInfo) { SetLastError(ERROR_INSUFFICIENT_BUFFER); return 0; } /* Copy each patch */ for(hfTemp = hf; hfTemp != NULL; hfTemp = hfTemp->hfPatch) { /* Get the file name and its length */ const char * szFileName = FileStream_GetFileName(hfTemp->ha->pStream); nLength = strlen(szFileName) + 1; /* Copy the file name */ memcpy(szFileInfo, szFileName, nLength * sizeof(char)); szFileInfo += nLength; } /* Make it multi-string */ szFileInfo[0] = 0; } return 1; }
static bool FindPatchPrefix_SC2_ArchiveName(TMPQArchive * haBase, TMPQArchive * haPatch) { const TCHAR * szPathBegin = FileStream_GetFileName(haBase->pStream); const TCHAR * szSeparator = NULL; const TCHAR * szPathEnd = szPathBegin + _tcslen(szPathBegin); const TCHAR * szPathPtr; int nSlashCount = 0; int nDotCount = 0; // Skip the part where the patch prefix would be too long if((szPathEnd - szPathBegin) > MAX_SC2_PATCH_PREFIX) szPathBegin = szPathEnd - MAX_SC2_PATCH_PREFIX; // Search for the file extension for(szPathPtr = szPathEnd; szPathPtr > szPathBegin; szPathPtr--) { if(szPathPtr[0] == _T('.')) { nDotCount++; break; } } // Search for the possible begin of the prefix name for(/* NOTHING */; szPathPtr > szPathBegin; szPathPtr--) { // Check the slashes, backslashes and hashes if(szPathPtr[0] == _T('\\') || szPathPtr[0] == _T('/') || szPathPtr[0] == _T('#')) { if(nDotCount == 0) return false; szSeparator = szPathPtr; nSlashCount++; } // Check the path parts if(szSeparator != NULL && nSlashCount >= nDotCount) { if(CheckPatchPrefix_SC2_ArchiveName(haPatch, szPathPtr, szSeparator, szPathEnd, _T("Battle.net"), 10)) return true; if(CheckPatchPrefix_SC2_ArchiveName(haPatch, szPathPtr, szSeparator, szPathEnd, _T("Campaigns"), 9)) return true; if(CheckPatchPrefix_SC2_ArchiveName(haPatch, szPathPtr, szSeparator, szPathEnd, _T("Mods"), 4)) return true; } } // Not matched, sorry return false; }
bool WINAPI SFileGetFileName(HANDLE hFile, char * szFileName) { TMPQFile * hf = (TMPQFile *)hFile; // MPQ File handle int nError = ERROR_INVALID_HANDLE; // Pre-zero the output buffer if(szFileName != NULL) *szFileName = 0; // Check valid parameters if(IsValidFileHandle(hFile)) { TFileEntry * pFileEntry = hf->pFileEntry; // For MPQ files, retrieve the file name from the file entry if(hf->pStream == NULL) { if(pFileEntry != NULL) { // If the file name is not there yet, create a pseudo name if(pFileEntry->szFileName == NULL) { nError = CreatePseudoFileName(hFile, pFileEntry, szFileName); } else { if(szFileName != NULL) strcpy(szFileName, pFileEntry->szFileName); nError = ERROR_SUCCESS; } } } // For local files, copy the file name from the stream else { if(szFileName != NULL) { const TCHAR * szStreamName = FileStream_GetFileName(hf->pStream); CopyFileName(szFileName, szStreamName, _tcslen(szStreamName)); } nError = ERROR_SUCCESS; } } if(nError != ERROR_SUCCESS) SetLastError(nError); return (nError == ERROR_SUCCESS); }
static bool FindPatchPrefix_SC2_HelperFile(TMPQArchive * haBase, TMPQArchive * haPatch) { TCHAR szHelperFile[MAX_PATH+1]; char szPatchPrefix[MAX_SC2_PATCH_PREFIX+0x41]; size_t nLength = 0; bool bResult = false; // Create the name of the patch helper file _tcscpy(szHelperFile, FileStream_GetFileName(haBase->pStream)); if(_tcslen(szHelperFile) + 6 > MAX_PATH) return false; _tcscat(szHelperFile, _T("-PATCH")); // Open the patch helper file and read the line if(ExtractPatchPrefixFromFile(szHelperFile, szPatchPrefix, MAX_SC2_PATCH_PREFIX, &nLength)) bResult = CheckAndCreatePatchPrefix(haPatch, szPatchPrefix, nLength); return bResult; }
int EXPORT_SYMBOL SFileGetFileName(void * hFile, char * szFileName) { TMPQFile * hf = (TMPQFile *)hFile; /* MPQ File handle */ int nError = ERROR_INVALID_HANDLE; /* Check valid parameters */ if(IsValidFileHandle(hFile)) { TFileEntry * pFileEntry = hf->pFileEntry; /* For MPQ files, retrieve the file name from the file entry */ if(hf->pStream == NULL) { if(pFileEntry != NULL) { /* If the file name is not there yet, create a pseudo name */ if(pFileEntry->szFileName == NULL) nError = CreatePseudoFileName(hFile, pFileEntry, szFileName); /* Copy the file name to the output buffer, if any */ if(pFileEntry->szFileName && szFileName) strcpy(szFileName, pFileEntry->szFileName); } } /* For local files, copy the file name from the stream */ else { if(szFileName != NULL) { const char * szStreamName = FileStream_GetFileName(hf->pStream); CopyFileName(szFileName, szStreamName, strlen(szStreamName)); } nError = ERROR_SUCCESS; } } if(nError != ERROR_SUCCESS) SetLastError(nError); return (nError == ERROR_SUCCESS); }
static bool CalculateMpqHashSha1( TMPQArchive * ha, PMPQ_SIGNATURE_INFO pSI, unsigned char * sha1_tail0, unsigned char * sha1_tail1, unsigned char * sha1_tail2) { ULONGLONG BeginBuffer; hash_state sha1_state_temp; hash_state sha1_state; LPBYTE pbDigestBuffer = NULL; char szPlainName[MAX_PATH]; // Allocate buffer for creating the MPQ digest. pbDigestBuffer = STORM_ALLOC(BYTE, MPQ_DIGEST_UNIT_SIZE); if(pbDigestBuffer == NULL) return false; // Initialize SHA1 state structure sha1_init(&sha1_state); // Calculate begin of data to be hashed BeginBuffer = pSI->BeginMpqData; // Create the digest for(;;) { ULONGLONG BytesRemaining; DWORD dwToRead = MPQ_DIGEST_UNIT_SIZE; // Check the number of bytes remaining BytesRemaining = pSI->EndMpqData - BeginBuffer; if(BytesRemaining < MPQ_DIGEST_UNIT_SIZE) dwToRead = (DWORD)BytesRemaining; if(dwToRead == 0) break; // Read the next chunk if(!FileStream_Read(ha->pStream, &BeginBuffer, pbDigestBuffer, dwToRead)) { STORM_FREE(pbDigestBuffer); return false; } // Pass the buffer to the hashing function sha1_process(&sha1_state, pbDigestBuffer, dwToRead); // Move pointers BeginBuffer += dwToRead; } // Add all three known tails and generate three hashes memcpy(&sha1_state_temp, &sha1_state, sizeof(hash_state)); sha1_done(&sha1_state_temp, sha1_tail0); memcpy(&sha1_state_temp, &sha1_state, sizeof(hash_state)); GetPlainAnsiFileName(FileStream_GetFileName(ha->pStream), szPlainName); AddTailToSha1(&sha1_state_temp, szPlainName); sha1_done(&sha1_state_temp, sha1_tail1); memcpy(&sha1_state_temp, &sha1_state, sizeof(hash_state)); AddTailToSha1(&sha1_state_temp, "ARCHIVE"); sha1_done(&sha1_state_temp, sha1_tail2); // Finalize the MD5 hash STORM_FREE(pbDigestBuffer); return true; }
bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bReserved */) { TFileStream * pTempStream = NULL; TMPQArchive * ha = (TMPQArchive *)hMpq; ULONGLONG ByteOffset; ULONGLONG ByteCount; LPDWORD pFileKeys = NULL; TCHAR szTempFile[MAX_PATH] = _T(""); TCHAR * szTemp = NULL; int nError = ERROR_SUCCESS; // Test the valid parameters if(!IsValidMpqHandle(hMpq)) nError = ERROR_INVALID_HANDLE; if(ha->dwFlags & MPQ_FLAG_READ_ONLY) nError = ERROR_ACCESS_DENIED; // If the MPQ is changed at this moment, we have to flush the archive if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_CHANGED)) { SFileFlushArchive(hMpq); } // Create the table with file keys if(nError == ERROR_SUCCESS) { if((pFileKeys = STORM_ALLOC(DWORD, ha->dwFileTableSize)) != NULL) memset(pFileKeys, 0, sizeof(DWORD) * ha->dwFileTableSize); else nError = ERROR_NOT_ENOUGH_MEMORY; } // First of all, we have to check of we are able to decrypt all files. // If not, sorry, but the archive cannot be compacted. if(nError == ERROR_SUCCESS) { // Initialize the progress variables for compact callback FileStream_GetSize(ha->pStream, &(ha->CompactTotalBytes)); ha->CompactBytesProcessed = 0; nError = CheckIfAllFilesKnown(ha, szListFile, pFileKeys); } // Get the temporary file name and create it if(nError == ERROR_SUCCESS) { _tcscpy(szTempFile, FileStream_GetFileName(ha->pStream)); if((szTemp = _tcsrchr(szTempFile, '.')) != NULL) _tcscpy(szTemp + 1, _T("mp_")); else _tcscat(szTempFile, _T("_")); pTempStream = FileStream_CreateFile(szTempFile, STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE); if(pTempStream == NULL) nError = GetLastError(); } // Write the data before MPQ user data (if any) if(nError == ERROR_SUCCESS && ha->UserDataPos != 0) { // Inform the application about the progress if(ha->pfnCompactCB != NULL) ha->pfnCompactCB(ha->pvCompactUserData, CCB_COPYING_NON_MPQ_DATA, ha->CompactBytesProcessed, ha->CompactTotalBytes); ByteOffset = 0; ByteCount = ha->UserDataPos; nError = CopyNonMpqData(ha, ha->pStream, pTempStream, ByteOffset, ByteCount); } // Write the MPQ user data (if any) if(nError == ERROR_SUCCESS && ha->MpqPos > ha->UserDataPos) { // At this point, we assume that the user data size is equal // to pUserData->dwHeaderOffs. // If this assumption doesn't work, then we have an unknown version of MPQ ByteOffset = ha->UserDataPos; ByteCount = ha->MpqPos - ha->UserDataPos; assert(ha->pUserData != NULL); assert(ha->pUserData->dwHeaderOffs == ByteCount); nError = CopyNonMpqData(ha, ha->pStream, pTempStream, ByteOffset, ByteCount); } // Write the MPQ header if(nError == ERROR_SUCCESS) { TMPQHeader SaveMpqHeader; // Write the MPQ header to the file memcpy(&SaveMpqHeader, ha->pHeader, ha->pHeader->dwHeaderSize); BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_1); BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_2); BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_3); BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_4); if(!FileStream_Write(pTempStream, NULL, &SaveMpqHeader, ha->pHeader->dwHeaderSize)) nError = GetLastError(); // Update the progress ha->CompactBytesProcessed += ha->pHeader->dwHeaderSize; } // Now copy all files if(nError == ERROR_SUCCESS) nError = CopyMpqFiles(ha, pFileKeys, pTempStream); // Defragment the file table if(nError == ERROR_SUCCESS) nError = RebuildFileTable(ha, ha->pHeader->dwHashTableSize, ha->dwMaxFileCount); // We also need to rebuild the HET table, if any if(nError == ERROR_SUCCESS) { // Invalidate (listfile) and (attributes) InvalidateInternalFiles(ha); // Rebuild the HET table, if we have any if(ha->pHetTable != NULL) nError = RebuildHetTable(ha); } // If succeeded, switch the streams if(nError == ERROR_SUCCESS) { if(FileStream_Replace(ha->pStream, pTempStream)) pTempStream = NULL; else nError = ERROR_CAN_NOT_COMPLETE; } // If all succeeded, save the MPQ tables if(nError == ERROR_SUCCESS) { // // Note: We don't recalculate position of the MPQ tables at this point. // SaveMPQTables does it automatically. // nError = SaveMPQTables(ha); if(nError == ERROR_SUCCESS && ha->pfnCompactCB != NULL) { ha->CompactBytesProcessed += (ha->pHeader->dwHashTableSize * sizeof(TMPQHash)); ha->CompactBytesProcessed += (ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock)); ha->pfnCompactCB(ha->pvCompactUserData, CCB_CLOSING_ARCHIVE, ha->CompactBytesProcessed, ha->CompactTotalBytes); } } // Cleanup and return if(pTempStream != NULL) FileStream_Close(pTempStream); if(pFileKeys != NULL) STORM_FREE(pFileKeys); if(nError != ERROR_SUCCESS) SetLastError(nError); return (nError == ERROR_SUCCESS); }
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 SFileGetFileInfo( HANDLE hMpqOrFile, SFileInfoClass InfoClass, void * pvFileInfo, DWORD cbFileInfo, LPDWORD pcbLengthNeeded) { MPQ_SIGNATURE_INFO SignatureInfo; TMPQArchive * ha = NULL; TFileEntry * pFileEntry = NULL; ULONGLONG Int64Value = 0; ULONGLONG ByteOffset = 0; TMPQHash * pHash; TMPQFile * hf = NULL; void * pvSrcFileInfo = NULL; DWORD cbSrcFileInfo = 0; DWORD dwInt32Value = 0; int nInfoType = SFILE_INFO_TYPE_INVALID_HANDLE; int nError = ERROR_SUCCESS; switch(InfoClass) { case SFileMpqFileName: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = (void *)FileStream_GetFileName(ha->pStream); cbSrcFileInfo = (DWORD)(_tcslen((TCHAR *)pvSrcFileInfo) + 1) * sizeof(TCHAR); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqStreamBitmap: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) return FileStream_GetBitmap(ha->pStream, pvFileInfo, cbFileInfo, pcbLengthNeeded); break; case SFileMpqUserDataOffset: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; if(ha->pUserData != NULL) { pvSrcFileInfo = &ha->UserDataPos; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } } break; case SFileMpqUserDataHeader: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; if(ha->pUserData != NULL) { ByteOffset = ha->UserDataPos; cbSrcFileInfo = sizeof(TMPQUserData); nInfoType = SFILE_INFO_TYPE_READ_FROM_FILE; } } break; case SFileMpqUserData: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; if(ha->pUserData != NULL) { ByteOffset = ha->UserDataPos + sizeof(TMPQUserData); cbSrcFileInfo = ha->pUserData->dwHeaderOffs - sizeof(TMPQUserData); nInfoType = SFILE_INFO_TYPE_READ_FROM_FILE; } } break; case SFileMpqHeaderOffset: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->MpqPos; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqHeaderSize: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->dwHeaderSize; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqHeader: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { ByteOffset = ha->MpqPos; cbSrcFileInfo = ha->pHeader->dwHeaderSize; nInfoType = SFILE_INFO_TYPE_READ_FROM_FILE; } break; case SFileMpqHetTableOffset: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->HetTablePos64; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqHetTableSize: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->HetTableSize64; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqHetHeader: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; pvSrcFileInfo = LoadExtTable(ha, ha->pHeader->HetTablePos64, (size_t)ha->pHeader->HetTableSize64, HET_TABLE_SIGNATURE, MPQ_KEY_HASH_TABLE); if(pvSrcFileInfo != NULL) { cbSrcFileInfo = sizeof(TMPQHetHeader); nInfoType = SFILE_INFO_TYPE_ALLOCATED; } } break; case SFileMpqHetTable: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; pvSrcFileInfo = LoadHetTable(ha); if(pvSrcFileInfo != NULL) { cbSrcFileInfo = sizeof(void *); nInfoType = SFILE_INFO_TYPE_TABLE_POINTER; } } break; case SFileMpqBetTableOffset: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->BetTablePos64; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqBetTableSize: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->BetTableSize64; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqBetHeader: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; pvSrcFileInfo = LoadExtTable(ha, ha->pHeader->BetTablePos64, (size_t)ha->pHeader->BetTableSize64, BET_TABLE_SIGNATURE, MPQ_KEY_BLOCK_TABLE); if(pvSrcFileInfo != NULL) { // It is allowed for the caller to only require BET header. cbSrcFileInfo = sizeof(TMPQBetHeader) + ((TMPQBetHeader *)pvSrcFileInfo)->dwFlagCount * sizeof(DWORD); if(cbFileInfo == sizeof(TMPQBetHeader)) cbSrcFileInfo = sizeof(TMPQBetHeader); nInfoType = SFILE_INFO_TYPE_ALLOCATED; } } break; case SFileMpqBetTable: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; pvSrcFileInfo = LoadBetTable(ha); if(pvSrcFileInfo != NULL) { cbSrcFileInfo = sizeof(void *); nInfoType = SFILE_INFO_TYPE_TABLE_POINTER; } } break; case SFileMpqHashTableOffset: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { Int64Value = MAKE_OFFSET64(ha->pHeader->wHashTablePosHi, ha->pHeader->dwHashTablePos); pvSrcFileInfo = &Int64Value; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqHashTableSize64: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->HashTableSize64; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqHashTableSize: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->dwHashTableSize; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqHashTable: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL && ha->pHashTable != NULL) { pvSrcFileInfo = ha->pHashTable; cbSrcFileInfo = ha->dwHashTableSize * sizeof(TMPQHash); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqBlockTableOffset: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { Int64Value = MAKE_OFFSET64(ha->pHeader->wBlockTablePosHi, ha->pHeader->dwBlockTablePos); pvSrcFileInfo = &Int64Value; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqBlockTableSize64: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->BlockTableSize64; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqBlockTableSize: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->dwBlockTableSize; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqBlockTable: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; if(MAKE_OFFSET64(ha->pHeader->wBlockTablePosHi, ha->pHeader->dwBlockTablePos) != 0) { cbSrcFileInfo = ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock); if(cbFileInfo >= cbSrcFileInfo) pvSrcFileInfo = LoadBlockTable(ha, true); nInfoType = SFILE_INFO_TYPE_ALLOCATED; } } break; case SFileMpqHiBlockTableOffset: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->HiBlockTablePos64; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqHiBlockTableSize64: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->HiBlockTableSize64; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqHiBlockTable: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; if(ha->pHeader->HiBlockTablePos64 && ha->pHeader->HiBlockTableSize64) { assert(false); } } break; case SFileMpqSignatures: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL && QueryMpqSignatureInfo(ha, &SignatureInfo)) { pvSrcFileInfo = &SignatureInfo.SignatureTypes; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqStrongSignatureOffset: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; if(QueryMpqSignatureInfo(ha, &SignatureInfo) && (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG)) { pvSrcFileInfo = &SignatureInfo.EndMpqData; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } } break; case SFileMpqStrongSignatureSize: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; if(QueryMpqSignatureInfo(ha, &SignatureInfo) && (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG)) { dwInt32Value = MPQ_STRONG_SIGNATURE_SIZE + 4; pvSrcFileInfo = &dwInt32Value; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } } break; case SFileMpqStrongSignature: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; if(QueryMpqSignatureInfo(ha, &SignatureInfo) && (SignatureInfo.SignatureTypes & SIGNATURE_TYPE_STRONG)) { pvSrcFileInfo = SignatureInfo.Signature; cbSrcFileInfo = MPQ_STRONG_SIGNATURE_SIZE + 4; nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } } break; case SFileMpqArchiveSize64: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->ArchiveSize64; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqArchiveSize: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->pHeader->dwArchiveSize; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqMaxFileCount: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->dwMaxFileCount; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqFileTableSize: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->dwFileTableSize; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqSectorSize: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &ha->dwSectorSize; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqNumberOfFiles: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { pvSrcFileInfo = &dwInt32Value; cbSrcFileInfo = sizeof(DWORD); dwInt32Value = GetMpqFileCount(ha); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqRawChunkSize: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { nInfoType = SFILE_INFO_TYPE_NOT_FOUND; if(ha->pHeader->dwRawChunkSize != 0) { pvSrcFileInfo = &ha->pHeader->dwRawChunkSize; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } } break; case SFileMpqStreamFlags: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { FileStream_GetFlags(ha->pStream, &dwInt32Value); pvSrcFileInfo = &dwInt32Value; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileMpqFlags: ha = IsValidMpqHandle(hMpqOrFile); if(ha != NULL) { dwInt32Value = ha->dwFlags; pvSrcFileInfo = &dwInt32Value; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileInfoPatchChain: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL) return GetFilePatchChain(hf, pvFileInfo, cbFileInfo, pcbLengthNeeded); break; case SFileInfoFileEntry: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->pFileEntry != NULL) { pvSrcFileInfo = pFileEntry = hf->pFileEntry; cbSrcFileInfo = sizeof(TFileEntry); if(pFileEntry->szFileName != NULL) cbSrcFileInfo += (DWORD)strlen(pFileEntry->szFileName) + 1; nInfoType = SFILE_INFO_TYPE_FILE_ENTRY; } break; case SFileInfoHashEntry: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->ha != NULL && hf->ha->pHashTable != NULL) { pvSrcFileInfo = hf->ha->pHashTable + hf->pFileEntry->dwHashIndex; cbSrcFileInfo = sizeof(TMPQHash); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileInfoHashIndex: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->pFileEntry != NULL) { pvSrcFileInfo = &hf->pFileEntry->dwHashIndex; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileInfoNameHash1: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->ha != NULL && hf->ha->pHashTable != NULL) { pHash = hf->ha->pHashTable + hf->pFileEntry->dwHashIndex; pvSrcFileInfo = &pHash->dwName1; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileInfoNameHash2: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->ha != NULL && hf->ha->pHashTable != NULL) { pHash = hf->ha->pHashTable + hf->pFileEntry->dwHashIndex; pvSrcFileInfo = &pHash->dwName2; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileInfoNameHash3: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->pFileEntry != NULL) { pvSrcFileInfo = &hf->pFileEntry->FileNameHash; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileInfoLocale: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->pFileEntry != NULL) { dwInt32Value = hf->pFileEntry->lcLocale; pvSrcFileInfo = &dwInt32Value; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileInfoFileIndex: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->ha != NULL && hf->pFileEntry != NULL) { dwInt32Value = (DWORD)(hf->pFileEntry - hf->ha->pFileTable); pvSrcFileInfo = &dwInt32Value; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileInfoByteOffset: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->pFileEntry != NULL) { pvSrcFileInfo = &hf->pFileEntry->ByteOffset; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileInfoFileTime: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->pFileEntry != NULL) { pvSrcFileInfo = &hf->pFileEntry->FileTime; cbSrcFileInfo = sizeof(ULONGLONG); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileInfoFileSize: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->pFileEntry != NULL) { pvSrcFileInfo = &hf->pFileEntry->dwFileSize; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileInfoCompressedSize: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->pFileEntry != NULL) { pvSrcFileInfo = &hf->pFileEntry->dwCmpSize; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileInfoFlags: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->pFileEntry != NULL) { pvSrcFileInfo = &hf->pFileEntry->dwFlags; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileInfoEncryptionKey: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL) { pvSrcFileInfo = &hf->dwFileKey; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; case SFileInfoEncryptionKeyRaw: hf = IsValidFileHandle(hMpqOrFile); if(hf != NULL && hf->pFileEntry != NULL) { dwInt32Value = hf->dwFileKey; if(hf->pFileEntry->dwFlags & MPQ_FILE_FIX_KEY) dwInt32Value = (dwInt32Value ^ hf->pFileEntry->dwFileSize) - (DWORD)hf->MpqFilePos; pvSrcFileInfo = &dwInt32Value; cbSrcFileInfo = sizeof(DWORD); nInfoType = SFILE_INFO_TYPE_DIRECT_POINTER; } break; default: // Invalid info class SetLastError(ERROR_INVALID_PARAMETER); return false; } // If we validated the handle and info class, give as much info as possible if(nInfoType >= SFILE_INFO_TYPE_DIRECT_POINTER) { // Give the length needed, if wanted if(pcbLengthNeeded != NULL) pcbLengthNeeded[0] = cbSrcFileInfo; // If the caller entered an output buffer, the output size must also be entered if(pvFileInfo != NULL && cbFileInfo != 0) { // Check if there is enough space in the output buffer if(cbSrcFileInfo <= cbFileInfo) { switch(nInfoType) { case SFILE_INFO_TYPE_DIRECT_POINTER: case SFILE_INFO_TYPE_ALLOCATED: assert(pvSrcFileInfo != NULL); memcpy(pvFileInfo, pvSrcFileInfo, cbSrcFileInfo); break; case SFILE_INFO_TYPE_READ_FROM_FILE: if(!FileStream_Read(ha->pStream, &ByteOffset, pvFileInfo, cbSrcFileInfo)) nError = GetLastError(); break; case SFILE_INFO_TYPE_TABLE_POINTER: assert(pvSrcFileInfo != NULL); *(void **)pvFileInfo = pvSrcFileInfo; pvSrcFileInfo = NULL; break; case SFILE_INFO_TYPE_FILE_ENTRY: assert(pFileEntry != NULL); ConvertFileEntryToSelfRelative((TFileEntry *)pvFileInfo, pFileEntry); break; } } else { nError = ERROR_INSUFFICIENT_BUFFER; } } // Free the file info if needed if(nInfoType == SFILE_INFO_TYPE_ALLOCATED && pvSrcFileInfo != NULL) STORM_FREE(pvSrcFileInfo); if(nInfoType == SFILE_INFO_TYPE_TABLE_POINTER && pvSrcFileInfo != NULL) SFileFreeFileInfo(pvSrcFileInfo, InfoClass); } else { // Handle error cases if(nInfoType == SFILE_INFO_TYPE_INVALID_HANDLE) nError = ERROR_INVALID_HANDLE; if(nInfoType == SFILE_INFO_TYPE_NOT_FOUND) nError = ERROR_FILE_NOT_FOUND; } // Set the last error value, if needed if(nError != ERROR_SUCCESS) SetLastError(nError); return (nError == ERROR_SUCCESS); }
int EXPORT_SYMBOL SFileCompactArchive(void * hMpq, const char * szListFile, int bReserved) { TFileStream * pTempStream = NULL; TMPQArchive * ha = (TMPQArchive *)hMpq; uint64_t ByteOffset; uint64_t ByteCount; uint32_t * pFileKeys = NULL; char szTempFile[1024] = ""; char * szTemp = NULL; int nError = ERROR_SUCCESS; /* Test the valid parameters */ if(!IsValidMpqHandle(hMpq)) nError = ERROR_INVALID_HANDLE; if(ha->dwFlags & MPQ_FLAG_READ_ONLY) nError = ERROR_ACCESS_DENIED; /* If the MPQ is changed at this moment, we have to flush the archive */ if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_CHANGED)) { SFileFlushArchive(hMpq); } /* Create the table with file keys */ if(nError == ERROR_SUCCESS) { if((pFileKeys = STORM_ALLOC(uint32_t, ha->dwFileTableSize)) != NULL) memset(pFileKeys, 0, sizeof(uint32_t) * ha->dwFileTableSize); else nError = ERROR_NOT_ENOUGH_MEMORY; } /* First of all, we have to check of we are able to decrypt all files. */ /* If not, sorry, but the archive cannot be compacted. */ if(nError == ERROR_SUCCESS) { /* Initialize the progress variables for compact callback */ FileStream_GetSize(ha->pStream, &(ha->CompactTotalBytes)); ha->CompactBytesProcessed = 0; nError = CheckIfAllKeysKnown(ha, szListFile, pFileKeys); } /* Get the temporary file name and create it */ if(nError == ERROR_SUCCESS) { strcpy(szTempFile, FileStream_GetFileName(ha->pStream)); if((szTemp = strrchr(szTempFile, '.')) != NULL) strcpy(szTemp + 1, "mp_"); else strcat(szTempFile, "_"); pTempStream = FileStream_CreateFile(szTempFile, STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE); if(pTempStream == NULL) nError = GetLastError(); } /* Write the data before MPQ user data (if any) */ if(nError == ERROR_SUCCESS && ha->UserDataPos != 0) { /* Inform the application about the progress */ if(ha->pfnCompactCB != NULL) ha->pfnCompactCB(ha->pvCompactUserData, CCB_COPYING_NON_MPQ_DATA, ha->CompactBytesProcessed, ha->CompactTotalBytes); ByteOffset = 0; ByteCount = ha->UserDataPos; nError = CopyNonMpqData(ha, ha->pStream, pTempStream, &ByteOffset, ByteCount); } /* Write the MPQ user data (if any) */ if(nError == ERROR_SUCCESS && ha->MpqPos > ha->UserDataPos) { /* At this point, we assume that the user data size is equal */ /* to pUserData->dwHeaderOffs. */ /* If this assumption doesn't work, then we have an unknown version of MPQ */ ByteOffset = ha->UserDataPos; ByteCount = ha->MpqPos - ha->UserDataPos; assert(ha->pUserData != NULL); assert(ha->pUserData->dwHeaderOffs == ByteCount); nError = CopyNonMpqData(ha, ha->pStream, pTempStream, &ByteOffset, ByteCount); } /* Write the MPQ header */ if(nError == ERROR_SUCCESS) { TMPQHeader SaveMpqHeader; /* Write the MPQ header to the file */ memcpy(&SaveMpqHeader, ha->pHeader, ha->pHeader->dwHeaderSize); BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_1); BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_2); BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_3); BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_4); if(!FileStream_Write(pTempStream, NULL, &SaveMpqHeader, ha->pHeader->dwHeaderSize)) nError = GetLastError(); /* Update the progress */ ha->CompactBytesProcessed += ha->pHeader->dwHeaderSize; } /* Now copy all files */ if(nError == ERROR_SUCCESS) nError = CopyMpqFiles(ha, pFileKeys, pTempStream); /* If succeeded, switch the streams */ if(nError == ERROR_SUCCESS) { ha->dwFlags |= MPQ_FLAG_CHANGED; if(FileStream_Replace(ha->pStream, pTempStream)) pTempStream = NULL; else nError = ERROR_CAN_NOT_COMPLETE; } /* Final user notification */ if(nError == ERROR_SUCCESS && ha->pfnCompactCB != NULL) { ha->CompactBytesProcessed += (ha->pHeader->dwHashTableSize * sizeof(TMPQHash)); ha->CompactBytesProcessed += (ha->dwFileTableSize * sizeof(TMPQBlock)); ha->pfnCompactCB(ha->pvCompactUserData, CCB_CLOSING_ARCHIVE, ha->CompactBytesProcessed, ha->CompactTotalBytes); } /* Cleanup and return */ if(pTempStream != NULL) FileStream_Close(pTempStream); if(pFileKeys != NULL) STORM_FREE(pFileKeys); if(nError != ERROR_SUCCESS) SetLastError(nError); return (nError == ERROR_SUCCESS); }