bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope) { TMPQArchive * ha = IsValidMpqHandle(hMpq); TMPQFile * hf = NULL; int nError = ERROR_SUCCESS; // Keep compiler happy dwSearchScope = dwSearchScope; // Check the parameters if(ha == NULL) nError = ERROR_INVALID_HANDLE; if(szFileName == NULL || *szFileName == 0) nError = ERROR_INVALID_PARAMETER; if(IsInternalMpqFileName(szFileName)) nError = ERROR_INTERNAL_FILE; // Do not allow to remove files from read-only or patched MPQs if(nError == ERROR_SUCCESS) { if((ha->dwFlags & MPQ_FLAG_READ_ONLY) || (ha->haPatch != NULL)) nError = ERROR_ACCESS_DENIED; } // If all checks have passed, we can delete the file from the MPQ if(nError == ERROR_SUCCESS) { // Open the file from the MPQ if(SFileOpenFileEx(hMpq, szFileName, SFILE_OPEN_BASE_FILE, (HANDLE *)&hf)) { // Delete the file entry nError = DeleteFileEntry(ha, hf); FreeFileHandle(hf); } else nError = GetLastError(); } // If the file has been deleted, we need to invalidate // the internal files and recreate HET table if(nError == ERROR_SUCCESS) { // Invalidate the entries for internal files // After we are done with MPQ changes, we need to re-create them anyway InvalidateInternalFiles(ha); // // Don't rebuild HET table now; the file's flags indicate // that it's been deleted, which is enough // } // Resolve error and exit if(nError != ERROR_SUCCESS) SetLastError(nError); return (nError == ERROR_SUCCESS); }
bool WINAPI SFileCloseFile(HANDLE hFile) { TMPQFile * hf = (TMPQFile *)hFile; if(!IsValidFileHandle(hFile)) { SetLastError(ERROR_INVALID_HANDLE); return false; } // Free the structure FreeFileHandle(hf); return true; }
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 SFileAddFile_Init( TMPQArchive * ha, TMPQFile * hfSrc, TMPQFile ** phf) { TFileEntry * pFileEntry = NULL; TMPQFile * hf = NULL; // File structure for newly added file ULONGLONG FileTime = hfSrc->pFileEntry->FileTime; DWORD dwFileSize = hfSrc->pFileEntry->dwFileSize; DWORD dwFlags = hfSrc->pFileEntry->dwFlags; int nError = ERROR_SUCCESS; // Allocate the TMPQFile entry for newly added file hf = CreateWritableHandle(ha, dwFileSize); if(hf == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; // We need to keep the file entry index the same like in the source archive // This is because multiple hash table entries can point to the same file entry if(nError == ERROR_SUCCESS) { // Retrieve the file entry for the target file pFileEntry = ha->pFileTable + (hfSrc->pFileEntry - hfSrc->ha->pFileTable); // Copy all variables except file name if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0) { pFileEntry[0] = hfSrc->pFileEntry[0]; pFileEntry->szFileName = NULL; } else nError = ERROR_ALREADY_EXISTS; // Set the file entry to the file structure hf->pFileEntry = pFileEntry; } // Prepare the pointer to hash table entry if(nError == ERROR_SUCCESS && ha->pHashTable != NULL && hfSrc->pHashEntry != NULL) { hf->dwHashIndex = (DWORD)(hfSrc->pHashEntry - hfSrc->ha->pHashTable); hf->pHashEntry = ha->pHashTable + hf->dwHashIndex; } // Prepare the file key (copy from source file) if(nError == ERROR_SUCCESS && (dwFlags & MPQ_FILE_ENCRYPTED)) { hf->dwFileKey = hfSrc->dwFileKey; if(hf->dwFileKey == 0) nError = ERROR_UNKNOWN_FILE_KEY; } // Fill the file entry and TMPQFile structure if(nError == ERROR_SUCCESS) { nError = FillWritableHandle(ha, hf, FileTime, dwFileSize, dwFlags); } // Free the file handle if failed if(nError != ERROR_SUCCESS && hf != NULL) FreeFileHandle(hf); // Give the handle to the caller *phf = hf; return nError; }
int SFileAddFile_Init( TMPQArchive * ha, const char * szFileName, ULONGLONG FileTime, DWORD dwFileSize, LCID lcLocale, DWORD dwFlags, TMPQFile ** phf) { TFileEntry * pFileEntry = NULL; TMPQFile * hf = NULL; // File structure for newly added file DWORD dwHashIndex = HASH_ENTRY_FREE; int nError = ERROR_SUCCESS; // // Note: This is an internal function so no validity checks are done. // It is the caller's responsibility to make sure that no invalid // flags get to this point // // Sestor CRC is not allowed with single unit files if(dwFlags & MPQ_FILE_SINGLE_UNIT) dwFlags &= ~MPQ_FILE_SECTOR_CRC; // Sector CRC is not allowed if the file is not compressed if(!(dwFlags & MPQ_FILE_COMPRESS_MASK)) dwFlags &= ~MPQ_FILE_SECTOR_CRC; // Fix Key is not allowed if the file is not enrypted if(!(dwFlags & MPQ_FILE_ENCRYPTED)) dwFlags &= ~MPQ_FILE_FIX_KEY; // If the MPQ is of version 3.0 or higher, we ignore file locale. // This is because HET and BET tables have no known support for it if(ha->pHeader->wFormatVersion >= MPQ_FORMAT_VERSION_3) lcLocale = 0; // Allocate the TMPQFile entry for newly added file hf = CreateWritableHandle(ha, dwFileSize); if(hf == NULL) return false; // Allocate file entry in the MPQ if(nError == ERROR_SUCCESS) { // Check if the file already exists in the archive pFileEntry = GetFileEntryExact(ha, szFileName, lcLocale, &dwHashIndex); if(pFileEntry != NULL) { if(dwFlags & MPQ_FILE_REPLACEEXISTING) InvalidateInternalFiles(ha); else nError = ERROR_ALREADY_EXISTS; } else { // Free all internal files - (listfile), (attributes), (signature) InvalidateInternalFiles(ha); // Attempt to allocate new file entry pFileEntry = AllocateFileEntry(ha, szFileName, lcLocale, &dwHashIndex); if(pFileEntry == NULL) nError = ERROR_DISK_FULL; } // Set the file entry to the file structure hf->pFileEntry = pFileEntry; } // Prepare the pointer to hash table entry if(nError == ERROR_SUCCESS && ha->pHashTable != NULL && dwHashIndex < ha->pHeader->dwHashTableSize) { hf->pHashEntry = ha->pHashTable + dwHashIndex; hf->pHashEntry->lcLocale = (USHORT)lcLocale; } // Prepare the file key if(nError == ERROR_SUCCESS && (dwFlags & MPQ_FILE_ENCRYPTED)) { hf->dwFileKey = DecryptFileKey(szFileName, hf->MpqFilePos, dwFileSize, dwFlags); if(hf->dwFileKey == 0) nError = ERROR_UNKNOWN_FILE_KEY; } // Fill the file entry and TMPQFile structure if(nError == ERROR_SUCCESS) { // At this point, the file name in the file entry must be set assert(pFileEntry->szFileName != NULL); assert(_stricmp(pFileEntry->szFileName, szFileName) == 0); nError = FillWritableHandle(ha, hf, FileTime, dwFileSize, dwFlags); } // Free the file handle if failed if(nError != ERROR_SUCCESS && hf != NULL) FreeFileHandle(hf); // Give the handle to the caller *phf = hf; return nError; }
// 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); }
bool WINAPI SFileOpenFileEx(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope, HANDLE * phFile) { TMPQArchive * ha = (TMPQArchive *)hMpq; TFileEntry * pFileEntry = NULL; TMPQFile * hf = NULL; DWORD dwFileIndex = 0; bool bOpenByIndex = false; int nError = ERROR_SUCCESS; // Don't accept NULL pointer to file handle if(phFile == NULL) nError = ERROR_INVALID_PARAMETER; // Prepare the file opening if(nError == ERROR_SUCCESS) { switch(dwSearchScope) { case SFILE_OPEN_FROM_MPQ: case SFILE_OPEN_BASE_FILE: if(!IsValidMpqHandle(hMpq)) { nError = ERROR_INVALID_HANDLE; break; } if(szFileName == NULL || *szFileName == 0) { nError = ERROR_INVALID_PARAMETER; break; } // Check the pseudo-file name if(IsPseudoFileName(szFileName, &dwFileIndex)) { pFileEntry = GetFileEntryByIndex(ha, dwFileIndex); bOpenByIndex = true; if(pFileEntry == NULL) nError = ERROR_FILE_NOT_FOUND; } else { // If this MPQ is a patched archive, open the file as patched if(ha->haPatch == NULL || dwSearchScope == SFILE_OPEN_BASE_FILE) { // Otherwise, open the file from *this* MPQ pFileEntry = GetFileEntryLocale(ha, szFileName, lcFileLocale); if(pFileEntry == NULL) nError = ERROR_FILE_NOT_FOUND; } else { return OpenPatchedFile(hMpq, szFileName, 0, phFile); } } break; case SFILE_OPEN_ANY_LOCALE: // This open option is reserved for opening MPQ internal listfile. // No argument validation. Tries to open file with neutral locale first, // then any other available. pFileEntry = GetFileEntryAny(ha, szFileName); if(pFileEntry == NULL) nError = ERROR_FILE_NOT_FOUND; break; case SFILE_OPEN_LOCAL_FILE: if(szFileName == NULL || *szFileName == 0) { nError = ERROR_INVALID_PARAMETER; break; } return OpenLocalFile(szFileName, phFile); default: // Don't accept any other value nError = ERROR_INVALID_PARAMETER; break; } // Quick return if something failed if(nError != ERROR_SUCCESS) { SetLastError(nError); *phFile = NULL; return false; } } // Test if the file was not already deleted. if(nError == ERROR_SUCCESS) { if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) == 0) nError = ERROR_FILE_NOT_FOUND; if(pFileEntry->dwFlags & ~MPQ_FILE_VALID_FLAGS) nError = ERROR_NOT_SUPPORTED; } // Allocate file handle if(nError == ERROR_SUCCESS) { hf = CreateFileHandle(ha, pFileEntry); if(hf == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; } // Initialize file handle if(nError == ERROR_SUCCESS) { // If the MPQ has sector CRC enabled, enable if for the file if(ha->dwFlags & MPQ_FLAG_CHECK_SECTOR_CRC) hf->bCheckSectorCRCs = true; // If we know the real file name, copy it to the file entry if(bOpenByIndex == false) { // If there is no file name yet, allocate it AllocateFileName(ha, pFileEntry, szFileName); // If the file is encrypted, we should detect the file key if(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED) { hf->dwFileKey = DecryptFileKey(szFileName, pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags); } } else { // Try to auto-detect the file name if(!SFileGetFileName(hf, NULL)) nError = GetLastError(); } } // Cleanup and exit if(nError != ERROR_SUCCESS) { SetLastError(nError); FreeFileHandle(hf); return false; } *phFile = hf; return true; }
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; }