bool WINAPI SFileSetMaxFileCount(HANDLE hMpq, DWORD dwMaxFileCount) { TMPQArchive * ha = (TMPQArchive *)hMpq; DWORD dwNewHashTableSize = 0; 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(dwMaxFileCount < ha->dwFileTableSize) nError = ERROR_DISK_FULL; // ALL file names must be known in order to be able // to rebuild hash table if(nError == ERROR_SUCCESS) { nError = CheckIfAllFilesKnown(ha, NULL, NULL); } // If the MPQ has a hash table, then we relocate the hash table if(nError == ERROR_SUCCESS) { // Calculate the hash table size for the new file limit dwNewHashTableSize = GetHashTableSizeForFileCount(dwMaxFileCount); // Rebuild both file tables nError = RebuildFileTable(ha, dwNewHashTableSize, dwMaxFileCount); } // We always have to rebuild the (attributes) file due to file table change 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); } // Return the error if(nError != ERROR_SUCCESS) SetLastError(nError); return (nError == ERROR_SUCCESS); }
int SFileAddFile_Finish(TMPQFile * hf) { TMPQArchive * ha = hf->ha; TFileEntry * pFileEntry = hf->pFileEntry; int nError = hf->nAddFileError; // If all previous operations succeeded, we can update the MPQ if(nError == ERROR_SUCCESS) { // Verify if the caller wrote the file properly if(hf->pPatchInfo == NULL) { assert(pFileEntry != NULL); if(hf->dwFilePos != pFileEntry->dwFileSize) nError = ERROR_CAN_NOT_COMPLETE; } else { if(hf->dwFilePos != hf->pPatchInfo->dwDataSize) nError = ERROR_CAN_NOT_COMPLETE; } } // Now we need to recreate the HET table, if exists if(nError == ERROR_SUCCESS && ha->pHetTable != NULL) { nError = RebuildHetTable(ha); } // Update the block table size if(nError == ERROR_SUCCESS) { // Call the user callback, if any if(ha->pfnAddFileCB != NULL) ha->pfnAddFileCB(ha->pvAddFileUserData, hf->dwDataSize, hf->dwDataSize, true); } else { // Free the file entry in MPQ tables if(pFileEntry != NULL) DeleteFileEntry(ha, hf); } // Clear the add file callback FreeFileHandle(hf); return nError; }
int EXPORT_SYMBOL SFileSetMaxFileCount(void * hMpq, uint32_t dwMaxFileCount) { TMPQArchive * ha = (TMPQArchive *)hMpq; uint32_t dwNewHashTableSize = 0; 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(dwMaxFileCount < ha->dwFileTableSize) nError = ERROR_DISK_FULL; /* ALL file names must be known in order to be able to rebuild hash table */ if(nError == ERROR_SUCCESS && ha->pHashTable != NULL) { nError = CheckIfAllFilesKnown(ha); if(nError == ERROR_SUCCESS) { /* Calculate the hash table size for the new file limit */ dwNewHashTableSize = GetHashTableSizeForFileCount(dwMaxFileCount); /* Rebuild both file tables */ nError = RebuildFileTable(ha, dwNewHashTableSize); } } /* We always have to rebuild the (attributes) file due to file table change */ 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); } /* Return the error */ if(nError != ERROR_SUCCESS) SetLastError(nError); return (nError == ERROR_SUCCESS); }
bool WINAPI SFileFlushArchive(HANDLE hMpq) { TMPQArchive * ha; int nResultError = ERROR_SUCCESS; int nError; // Do nothing if 'hMpq' is bad parameter if((ha = IsValidMpqHandle(hMpq)) == NULL) { SetLastError(ERROR_INVALID_HANDLE); return false; } // Only if the MPQ was changed if(ha->dwFlags & MPQ_FLAG_CHANGED) { // Indicate that we are saving MPQ internal structures ha->dwFlags |= MPQ_FLAG_SAVING_TABLES; // Defragment the file table. This will allow us to put the internal files to the end DefragmentFileTable(ha); // // Create each internal file // Note that the (signature) file is usually before (listfile) in the file table // if(ha->dwFlags & MPQ_FLAG_SIGNATURE_NEW) { nError = SSignFileCreate(ha); if(nError != ERROR_SUCCESS) nResultError = nError; } if(ha->dwFlags & MPQ_FLAG_LISTFILE_NEW) { nError = SListFileSaveToMpq(ha); if(nError != ERROR_SUCCESS) nResultError = nError; } if(ha->dwFlags & MPQ_FLAG_ATTRIBUTES_NEW) { nError = SAttrFileSaveToMpq(ha); if(nError != ERROR_SUCCESS) nResultError = nError; } // Save HET table, BET table, hash table, block table, hi-block table if(ha->dwFlags & MPQ_FLAG_CHANGED) { // Rebuild the HET table if(ha->pHetTable != NULL) RebuildHetTable(ha); // Save all MPQ tables first nError = SaveMPQTables(ha); if(nError != ERROR_SUCCESS) nResultError = nError; // If the archive has weak signature, we need to finish it if(ha->dwFileFlags3 != 0) { nError = SSignFileFinish(ha); if(nError != ERROR_SUCCESS) nResultError = nError; } } // We are no longer saving internal MPQ structures ha->dwFlags &= ~MPQ_FLAG_SAVING_TABLES; } // Return the error if(nResultError != ERROR_SUCCESS) SetLastError(nResultError); return (nResultError == 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); }
// Renames the file within the archive. bool WINAPI SFileRenameFile(HANDLE hMpq, const char * szFileName, const char * szNewFileName) { TMPQArchive * ha = IsValidMpqHandle(hMpq); TMPQFile * hf; int nError = ERROR_SUCCESS; // Test the valid parameters if(ha == NULL) nError = ERROR_INVALID_HANDLE; if(szFileName == NULL || *szFileName == 0 || szNewFileName == NULL || *szNewFileName == 0) nError = ERROR_INVALID_PARAMETER; if(IsInternalMpqFileName(szFileName) || IsInternalMpqFileName(szNewFileName)) nError = ERROR_INTERNAL_FILE; // Do not allow to rename files in MPQ open for read only if(nError == ERROR_SUCCESS) { if(ha->dwFlags & MPQ_FLAG_READ_ONLY) nError = ERROR_ACCESS_DENIED; } // Open the new file. If exists, we don't allow rename operation if(nError == ERROR_SUCCESS) { if(GetFileEntryLocale(ha, szNewFileName, lcFileLocale) != NULL) nError = ERROR_ALREADY_EXISTS; } // Open the file from the MPQ if(nError == ERROR_SUCCESS) { // Attempt to open the file if(SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_BASE_FILE, (HANDLE *)&hf)) { ULONGLONG RawDataOffs; TFileEntry * pFileEntry = hf->pFileEntry; // Invalidate the entries for internal files InvalidateInternalFiles(ha); // Rename the file entry in the table nError = RenameFileEntry(ha, hf, szNewFileName); // If the file is encrypted, we have to re-crypt the file content // with the new decryption key if((nError == ERROR_SUCCESS) && (pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED)) { // Recrypt the file data in the MPQ nError = RecryptFileData(ha, hf, szFileName, szNewFileName); // Update the MD5 of the raw block if(nError == ERROR_SUCCESS && ha->pHeader->dwRawChunkSize != 0) { RawDataOffs = ha->MpqPos + pFileEntry->ByteOffset; WriteMpqDataMD5(ha->pStream, RawDataOffs, pFileEntry->dwCmpSize, ha->pHeader->dwRawChunkSize); } } // Free the file handle FreeFileHandle(hf); } else { nError = GetLastError(); } } // We also need to rebuild the HET table, if present if(nError == ERROR_SUCCESS && ha->pHetTable != NULL) nError = RebuildHetTable(ha); // Resolve error and exit if(nError != ERROR_SUCCESS) SetLastError(nError); return (nError == ERROR_SUCCESS); }