bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bReserved */) { TFileStream * pTempStream = NULL; TMPQArchive * ha = (TMPQArchive *)hMpq; ULONGLONG ByteOffset; ULONGLONG ByteCount; LPDWORD pFileKeys = NULL; char szTempFile[MAX_PATH] = ""; char * szTemp = NULL; int nError = ERROR_SUCCESS; // Test the valid parameters if (!IsValidMpqHandle(ha)) nError = ERROR_INVALID_HANDLE; if (ha->dwFlags & MPQ_FLAG_READ_ONLY) nError = ERROR_ACCESS_DENIED; if (ha->pHetTable != NULL || ha->pBetTable != NULL) nError = ERROR_ACCESS_DENIED; // Create the table with file keys if (nError == ERROR_SUCCESS) { if ((pFileKeys = ALLOCMEM(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, CompactTotalBytes); CompactBytesProcessed = 0; nError = CheckIfAllFilesKnown(ha, szListFile, pFileKeys); } // Get the temporary file name and create it if (nError == ERROR_SUCCESS) { strcpy(szTempFile, ha->pStream->szFileName); if ((szTemp = strrchr(szTempFile, '.')) != NULL) strcpy(szTemp + 1, "mp_"); else strcat(szTempFile, "_"); pTempStream = FileStream_CreateFile(szTempFile); 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 (CompactCB != NULL) CompactCB(pvUserData, CCB_COPYING_NON_MPQ_DATA, CompactBytesProcessed, CompactTotalBytes); ByteOffset = 0; ByteCount = ha->UserDataPos; nError = CopyNonMpqData(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->pStream, pTempStream, ByteOffset, ByteCount); } // Write the MPQ header if (nError == ERROR_SUCCESS) { // Remember the header size before swapping DWORD dwBytesToWrite = ha->pHeader->dwHeaderSize; BSWAP_TMPQHEADER(ha->pHeader); if (!FileStream_Write(pTempStream, NULL, ha->pHeader, dwBytesToWrite)) nError = GetLastError(); BSWAP_TMPQHEADER(ha->pHeader); // Update the progress CompactBytesProcessed += ha->pHeader->dwHeaderSize; ha->dwFlags &= ~MPQ_FLAG_NO_HEADER; } // Now copy all files if (nError == ERROR_SUCCESS) { nError = CopyMpqFiles(ha, pFileKeys, pTempStream); } // If succeeded, switch the streams if (nError == ERROR_SUCCESS) { if (FileStream_MoveFile(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 && CompactCB != NULL) { CompactBytesProcessed += (ha->pHeader->dwHashTableSize * sizeof(TMPQHash)); CompactBytesProcessed += (ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock)); CompactCB(pvUserData, CCB_CLOSING_ARCHIVE, CompactBytesProcessed, CompactTotalBytes); } } // Invalidate the compact callback pvUserData = NULL; CompactCB = NULL; // Cleanup and return if (pTempStream != NULL) FileStream_Close(pTempStream); if (pFileKeys != NULL) FREEMEM(pFileKeys); if (nError != ERROR_SUCCESS) SetLastError(nError); return (nError == ERROR_SUCCESS); }
BOOL WINAPI SFileCompactArchive(HANDLE hMPQ, const char * szListFile, BOOL /* bReserved */) { TMPQArchive * ha = (TMPQArchive *)hMPQ; HANDLE hFile = INVALID_HANDLE_VALUE; DWORD * pFileSeeds = NULL; char szTempFile[MAX_PATH] = ""; char * szTemp = NULL; DWORD dwTransferred; int nError = ERROR_SUCCESS; // Test the valid parameters if(!IsValidMpqHandle(ha)) nError = ERROR_INVALID_PARAMETER; // Create the table with file seeds if(nError == ERROR_SUCCESS) { if((pFileSeeds = ALLOCMEM(DWORD, ha->pHeader->dwBlockTableSize)) != NULL) memset(pFileSeeds, 0, sizeof(DWORD) * ha->pHeader->dwBlockTableSize); 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) nError = CheckIfAllFilesKnown(ha, szListFile, pFileSeeds); // Get the temporary file name and create it if(nError == ERROR_SUCCESS) { if(CompactCB != NULL) CompactCB(lpUserData, CCB_COPYING_NON_MPQ_DATA, 0, 0); strcpy(szTempFile, ha->szFileName); if((szTemp = strrchr(szTempFile, '.')) != NULL) strcpy(szTemp + 1, "mp_"); else strcat(szTempFile, "_"); hFile = CreateFile(szTempFile, GENERIC_READ|GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, 0, NULL); if(hFile == INVALID_HANDLE_VALUE) nError = GetLastError(); } // Write the data before MPQ header (if any) if(nError == ERROR_SUCCESS && ha->MpqPos.QuadPart > 0) { SetFilePointer(ha->hFile, 0, NULL, FILE_BEGIN); if(ha->pShunt != NULL) nError = CopyNonMpqData(ha->hFile, hFile, ha->ShuntPos); else nError = CopyNonMpqData(ha->hFile, hFile, ha->MpqPos); } // Write the MPQ shunt (if any) if(nError == ERROR_SUCCESS && ha->pShunt != NULL) { BSWAP_TMPQSHUNT(ha->pShunt); WriteFile(hFile, ha->pShunt, sizeof(TMPQShunt), &dwTransferred, NULL); BSWAP_TMPQSHUNT(ha->pShunt); if(dwTransferred != sizeof(TMPQShunt)) nError = ERROR_DISK_FULL; } // Write the data between MPQ shunt and the MPQ header (if any) if(nError == ERROR_SUCCESS && ha->pShunt != NULL) { LARGE_INTEGER BytesToCopy; BytesToCopy.QuadPart = ha->MpqPos.QuadPart - (ha->ShuntPos.QuadPart + sizeof(TMPQShunt)); nError = CopyNonMpqData(ha->hFile, hFile, BytesToCopy); } // Write the MPQ header if(nError == ERROR_SUCCESS) { BSWAP_TMPQHEADER(ha->pHeader); WriteFile(hFile, ha->pHeader, ha->pHeader->dwHeaderSize, &dwTransferred, NULL); BSWAP_TMPQHEADER(ha->pHeader); if(dwTransferred != ha->pHeader->dwHeaderSize) nError = ERROR_DISK_FULL; } // Write the data between the header and between the first file // For this, we have to determine where the first file begins if(nError == ERROR_SUCCESS) { LARGE_INTEGER FirstFilePos; LARGE_INTEGER TempPos; TMPQBlockEx * pBlockEx = ha->pExtBlockTable; TMPQBlock * pBlockEnd = ha->pBlockTable + ha->pHeader->dwBlockTableSize; TMPQBlock * pBlock = ha->pBlockTable; // Maximum file position FirstFilePos.HighPart = 0x7FFFFFFF; FirstFilePos.LowPart = 0xFFFFFFFF; // Find the block with the least position in the MPQ while(pBlock < pBlockEnd) { TempPos.HighPart = pBlockEx->wFilePosHigh; TempPos.LowPart = pBlock->dwFilePos; if(TempPos.QuadPart < FirstFilePos.QuadPart) FirstFilePos = TempPos; pBlockEx++; pBlock++; } // Set the position in the source file right after the file header TempPos.QuadPart = ha->MpqPos.QuadPart + ha->pHeader->dwHeaderSize; SetFilePointer(ha->hFile, TempPos.LowPart, &TempPos.HighPart, FILE_BEGIN); // Get the number of bytes to copy FirstFilePos.QuadPart -= ha->pHeader->dwHeaderSize; nError = CopyNonMpqData(ha->hFile, hFile, FirstFilePos); } // Now write all file blocks. if(nError == ERROR_SUCCESS) nError = CopyMpqFiles(hFile, ha, pFileSeeds); // Now we need to update the tables positions // (but only if the tables are at the end of the file) if(nError == ERROR_SUCCESS) { LARGE_INTEGER RelativePos; LARGE_INTEGER FilePos = {0}; // Set the hash table position FilePos.LowPart = SetFilePointer(hFile, 0, &FilePos.HighPart, FILE_CURRENT); RelativePos.QuadPart = FilePos.QuadPart - ha->MpqPos.QuadPart; ha->pHeader->wHashTablePosHigh = (USHORT)RelativePos.HighPart; ha->pHeader->dwHashTablePos = RelativePos.LowPart; ha->HashTablePos = FilePos; // Set the block table position RelativePos.QuadPart += ha->pHeader->dwHashTableSize * sizeof(TMPQHash); FilePos.QuadPart += ha->pHeader->dwHashTableSize * sizeof(TMPQHash); ha->pHeader->wBlockTablePosHigh = (USHORT)RelativePos.HighPart; ha->pHeader->dwBlockTablePos = RelativePos.LowPart; ha->BlockTablePos = FilePos; // Set the extended block table position RelativePos.QuadPart += ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock); FilePos.QuadPart += ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock); if(ha->ExtBlockTablePos.QuadPart != 0) { ha->pHeader->ExtBlockTablePos = RelativePos; ha->ExtBlockTablePos = FilePos; RelativePos.QuadPart += ha->pHeader->dwBlockTableSize * sizeof(TMPQBlockEx); FilePos.QuadPart += ha->pHeader->dwBlockTableSize * sizeof(TMPQBlockEx); } // Set the archive size ha->pHeader->dwArchiveSize = RelativePos.LowPart; ha->MpqSize = RelativePos; } // If succeeded, update the tables in the file if(nError == ERROR_SUCCESS) { CloseHandle(ha->hFile); ha->hFile = hFile; hFile = INVALID_HANDLE_VALUE; nError = SaveMPQTables(ha); } // If all succeeded, switch the archives if(nError == ERROR_SUCCESS) { if(CompactCB != NULL) CompactCB(lpUserData, CCB_CLOSING_ARCHIVE, 0, 0); if(!DeleteFile(ha->szFileName) || // Delete the old archive !CloseHandle(ha->hFile) || // Close the new archive !MoveFile(szTempFile, ha->szFileName)) // Rename the temporary archive nError = GetLastError(); } // Now open the freshly renamed archive file if(nError == ERROR_SUCCESS) { ha->hFile = CreateFile(ha->szFileName, GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL); if(ha->hFile == INVALID_HANDLE_VALUE) nError = GetLastError(); } // Invalidate the positions of the archive if(nError == ERROR_SUCCESS) { ha->pLastFile = NULL; ha->dwBlockPos = 0; ha->dwBuffPos = 0; } // Cleanup and return if(hFile != INVALID_HANDLE_VALUE) CloseHandle(hFile); if(pFileSeeds != NULL) FREEMEM(pFileSeeds); if(nError != ERROR_SUCCESS) SetLastError(nError); DeleteFile(szTempFile); CompactCB = NULL; return (nError == ERROR_SUCCESS); }
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); }
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); }