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 SFileCreateFile( HANDLE hMpq, const char * szArchivedName, ULONGLONG FileTime, DWORD dwFileSize, LCID lcLocale, DWORD dwFlags, HANDLE * phFile) { TMPQArchive * ha = (TMPQArchive *)hMpq; int nError = ERROR_SUCCESS; // Check valid parameters if(!IsValidMpqHandle(hMpq)) nError = ERROR_INVALID_HANDLE; if(szArchivedName == NULL || *szArchivedName == 0) nError = ERROR_INVALID_PARAMETER; if(phFile == NULL) nError = ERROR_INVALID_PARAMETER; // Don't allow to add file if the MPQ is open for read only if(nError == ERROR_SUCCESS) { if(ha->dwFlags & MPQ_FLAG_READ_ONLY) nError = ERROR_ACCESS_DENIED; // Don't allow to add a file under pseudo-file name if(IsPseudoFileName(szArchivedName, NULL)) nError = ERROR_INVALID_PARAMETER; // Don't allow to add any of the internal files if(IsInternalMpqFileName(szArchivedName)) nError = ERROR_INTERNAL_FILE; } // Perform validity check of the MPQ flags if(nError == ERROR_SUCCESS) { // Mask all unsupported flags out dwFlags &= MPQ_FILE_VALID_FLAGS; // Check for valid flag combinations if((dwFlags & (MPQ_FILE_IMPLODE | MPQ_FILE_COMPRESS)) == (MPQ_FILE_IMPLODE | MPQ_FILE_COMPRESS)) nError = ERROR_INVALID_PARAMETER; } // Initiate the add file operation if(nError == ERROR_SUCCESS) nError = SFileAddFile_Init(ha, szArchivedName, FileTime, dwFileSize, lcLocale, dwFlags, (TMPQFile **)phFile); // Deal with the errors if(nError != ERROR_SUCCESS) SetLastError(nError); return (nError == ERROR_SUCCESS); }
bool WINAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale) { TMPQArchive * ha; TFileEntry * pFileEntry; TMPQFile * hf = IsValidFileHandle(hFile); // Invalid handle => do nothing if(hf == NULL) { SetLastError(ERROR_INVALID_HANDLE); return false; } // Do not allow to rename files in MPQ open for read only ha = hf->ha; if(ha->dwFlags & MPQ_FLAG_READ_ONLY) { SetLastError(ERROR_ACCESS_DENIED); return false; } // Do not allow unnamed access if(hf->pFileEntry->szFileName == NULL) { SetLastError(ERROR_CAN_NOT_COMPLETE); return false; } // Do not allow to change locale of any internal file if(IsInternalMpqFileName(hf->pFileEntry->szFileName)) { SetLastError(ERROR_INTERNAL_FILE); return false; } // Do not allow changing file locales if there is no hash table if(hf->pHashEntry == NULL) { SetLastError(ERROR_NOT_SUPPORTED); return false; } // We have to check if the file+locale is not already there pFileEntry = GetFileEntryExact(ha, hf->pFileEntry->szFileName, lcNewLocale, NULL); if(pFileEntry != NULL) { SetLastError(ERROR_ALREADY_EXISTS); return false; } // Update the locale in the hash table entry hf->pHashEntry->lcLocale = (USHORT)lcNewLocale; ha->dwFlags |= MPQ_FLAG_CHANGED; return true; }
bool WINAPI SFileSetFileLocale(HANDLE hFile, LCID lcNewLocale) { TMPQArchive * ha; TMPQFile * hf = (TMPQFile *)hFile; TMPQHash * pHashEnd; TMPQHash * pHash; // Invalid handle => do nothing if(!IsValidFileHandle(hf)) { SetLastError(ERROR_INVALID_HANDLE); return false; } // Do not allow to change locale of any internal file if(IsInternalMpqFileName(hf->szFileName)) { SetLastError(ERROR_INTERNAL_FILE); return false; } // Do not allow to rename files in MPQ open for read only ha = hf->ha; if(ha->dwFlags & MPQ_FLAG_READ_ONLY) { SetLastError(ERROR_ACCESS_DENIED); return false; } // If the file already has that locale, return OK if(hf->pHash->lcLocale == lcNewLocale) return true; // We have to check if the file+locale is not already there pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize; for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++) { if(pHash->dwName1 == hf->pHash->dwName1 && pHash->dwName2 == hf->pHash->dwName2 && pHash->lcLocale == lcNewLocale) { SetLastError(ERROR_ALREADY_EXISTS); return false; } } // Set the locale and return success hf->pHash->lcLocale = (USHORT)lcNewLocale; hf->ha->dwFlags |= MPQ_FLAG_CHANGED; return true; }
// Saves the whole listfile into the MPQ. int SListFileSaveToMpq(TMPQArchive * ha) { TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; TFileEntry * pFileEntry; TMPQFile * hf = NULL; char * szPrevItem; char ** SortTable = NULL; DWORD dwFileSize = 0; size_t nFileNodes = 0; size_t i; int nError = ERROR_SUCCESS; // Allocate the table for sorting listfile SortTable = STORM_ALLOC(char*, ha->dwFileTableSize); if(SortTable == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Construct the sort table // Note: in MPQs with multiple locale versions of the same file, // this code causes adding multiple listfile entries. // Since those MPQs were last time used in Starcraft, // we leave it as it is. for(pFileEntry = ha->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++) { // Only take existing items if((pFileEntry->dwFlags & MPQ_FILE_EXISTS) && pFileEntry->szFileName != NULL) { // Ignore pseudo-names if(!IsPseudoFileName(pFileEntry->szFileName, NULL) && !IsInternalMpqFileName(pFileEntry->szFileName)) { SortTable[nFileNodes++] = pFileEntry->szFileName; } } } // Sort the table qsort(SortTable, nFileNodes, sizeof(char *), CompareFileNodes); // Now parse the table of file names again - remove duplicates // and count file size. if(nFileNodes != 0) { // Count the 0-th item dwFileSize += (DWORD)strlen(SortTable[0]) + 2; szPrevItem = SortTable[0]; // Count all next items for(i = 1; i < nFileNodes; i++) { // If the item is the same like the last one, skip it if(_stricmp(SortTable[i], szPrevItem)) { dwFileSize += (DWORD)strlen(SortTable[i]) + 2; szPrevItem = SortTable[i]; } } // Determine the flags for (listfile) if(ha->dwFileFlags1 == 0) ha->dwFileFlags1 = GetDefaultSpecialFileFlags(ha, dwFileSize); // Create the listfile in the MPQ nError = SFileAddFile_Init(ha, LISTFILE_NAME, 0, dwFileSize, LANG_NEUTRAL, ha->dwFileFlags1 | MPQ_FILE_REPLACEEXISTING, &hf); // Add all file names if(nError == ERROR_SUCCESS) { // Each name is followed by newline ("\x0D\x0A") szPrevItem = SortTable[0]; nError = WriteListFileLine(hf, SortTable[0]); // Count all next items for(i = 1; i < nFileNodes; i++) { // If the item is the same like the last one, skip it if(_stricmp(SortTable[i], szPrevItem)) { WriteListFileLine(hf, SortTable[i]); szPrevItem = SortTable[i]; } } } } else { // Create the listfile in the MPQ dwFileSize = (DWORD)strlen(LISTFILE_NAME) + 2; nError = SFileAddFile_Init(ha, LISTFILE_NAME, 0, dwFileSize, LANG_NEUTRAL, MPQ_FILE_ENCRYPTED | MPQ_FILE_COMPRESS | MPQ_FILE_REPLACEEXISTING, &hf); // Just add "(listfile)" there if(nError == ERROR_SUCCESS) { WriteListFileLine(hf, LISTFILE_NAME); } } // Finalize the file in the MPQ if(hf != NULL) { SFileAddFile_Finish(hf); } // Free buffers if(nError == ERROR_SUCCESS) ha->dwFlags &= ~MPQ_FLAG_INV_LISTFILE; if(SortTable != NULL) STORM_FREE(SortTable); return nError; }
bool WINAPI SFileRemoveFile(HANDLE hMpq, const char * szFileName, DWORD dwSearchScope) { TMPQArchive * ha = (TMPQArchive *)hMpq; TMPQBlockEx * pBlockEx = NULL; // Block entry of deleted file TMPQBlock * pBlock = NULL; // Block entry of deleted file TMPQHash * pHash = NULL; // Hash entry of deleted file DWORD dwBlockIndex = 0; int nError = ERROR_SUCCESS; // Check the parameters if(nError == ERROR_SUCCESS) { if(!IsValidMpqHandle(ha)) nError = ERROR_INVALID_HANDLE; if(dwSearchScope != SFILE_OPEN_BY_INDEX && *szFileName == 0) nError = ERROR_INVALID_PARAMETER; } if(nError == ERROR_SUCCESS) { // Do not allow to remove files from MPQ open for read only if(ha->dwFlags & MPQ_FLAG_READ_ONLY) nError = ERROR_ACCESS_DENIED; // Do not allow to remove internal files if(dwSearchScope != SFILE_OPEN_BY_INDEX) { if(IsInternalMpqFileName(szFileName)) nError = ERROR_INTERNAL_FILE; } } // Get hash entry belonging to this file if(nError == ERROR_SUCCESS) { if(dwSearchScope == SFILE_OPEN_FROM_MPQ) { if((pHash = GetHashEntryExact(ha, (char *)szFileName, lcFileLocale)) == NULL) nError = ERROR_FILE_NOT_FOUND; } else { if((pHash = GetHashEntryByIndex(ha, (DWORD)(DWORD_PTR)szFileName)) == NULL) nError = ERROR_FILE_NOT_FOUND; } } // If index was not found, or is greater than number of files, exit. if(nError == ERROR_SUCCESS) { if((dwBlockIndex = pHash->dwBlockIndex) > ha->pHeader->dwBlockTableSize) nError = ERROR_FILE_NOT_FOUND; } // Get block and test if the file is not already deleted if(nError == ERROR_SUCCESS) { pBlockEx = ha->pExtBlockTable + dwBlockIndex; pBlock = ha->pBlockTable + dwBlockIndex; if((pBlock->dwFlags & MPQ_FILE_EXISTS) == 0) nError = ERROR_FILE_NOT_FOUND; } // Remove the file from the list file if(nError == ERROR_SUCCESS) nError = SListFileRemoveNode(ha, pHash); if(nError == ERROR_SUCCESS) { // Now invalidate the block entry and the hash entry. Do not make any // relocations and file copying, use SFileCompactArchive for it. pBlockEx->wFilePosHigh = 0; pBlock->dwFilePos = 0; pBlock->dwFSize = 0; pBlock->dwCSize = 0; pBlock->dwFlags = 0; pHash->dwName1 = 0xFFFFFFFF; pHash->dwName2 = 0xFFFFFFFF; pHash->lcLocale = 0xFFFF; pHash->wPlatform = 0xFFFF; pHash->dwBlockIndex = HASH_ENTRY_DELETED; // Note: We don't decrease size of the block table, // even if this was the last blocktable entry. // Update MPQ archive ha->dwFlags |= MPQ_FLAG_CHANGED; } // Resolve error and exit 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 = (TMPQArchive *)hMpq; TMPQBlock * pBlock; TMPQHash * pOldHash = NULL; // Hash entry for the original file TMPQHash * pNewHash = NULL; // Hash entry for the renamed file TMPQFile * hf; DWORD dwOldBlockIndex = 0; LCID lcSaveLocale = 0; int nError = ERROR_SUCCESS; // Test the valid parameters if(nError == ERROR_SUCCESS) { if(!IsValidMpqHandle(ha)) nError = ERROR_INVALID_HANDLE; if(szFileName == NULL || *szFileName == 0 || szNewFileName == NULL || *szNewFileName == 0) nError = ERROR_INVALID_PARAMETER; } if(nError == ERROR_SUCCESS) { // Do not allow to rename files in MPQ open for read only if(ha->dwFlags & MPQ_FLAG_READ_ONLY) nError = ERROR_ACCESS_DENIED; // Do not allow to rename any of the internal files // Also do not allow to rename any of files to an internal file if(IsInternalMpqFileName(szFileName) || IsInternalMpqFileName(szNewFileName)) nError = ERROR_INTERNAL_FILE; } // Get the hash table entry for the original file if(nError == ERROR_SUCCESS) { if((pOldHash = GetHashEntryExact(ha, szFileName, lcFileLocale)) == NULL) nError = ERROR_FILE_NOT_FOUND; } // Test if the file already exists in the archive if(nError == ERROR_SUCCESS) { if((pNewHash = GetHashEntryExact(ha, szNewFileName, lcFileLocale)) != NULL) nError = ERROR_ALREADY_EXISTS; } // We have to know the decryption key, otherwise we cannot re-crypt // the file after renaming if(nError == ERROR_SUCCESS) { // Save block table index and remove the hash table entry dwOldBlockIndex = pOldHash->dwBlockIndex; lcSaveLocale = pOldHash->lcLocale; pBlock = ha->pBlockTable + dwOldBlockIndex; // If the file is encrypted, we have to re-crypt the file content // with the new decryption key if(pBlock->dwFlags & MPQ_FILE_ENCRYPTED) { hf = CreateMpqFile(ha, "<renaming>"); if(hf != NULL) { hf->pHash = pOldHash; hf->pBlock = ha->pBlockTable + dwOldBlockIndex; hf->pBlockEx = ha->pExtBlockTable + dwOldBlockIndex; nError = RecryptFileData(ha, hf, szFileName, szNewFileName); FreeMPQFile(hf); } else { nError = ERROR_NOT_ENOUGH_MEMORY; } } } // Get the hash table entry for the renamed file if(nError == ERROR_SUCCESS) { SListFileRemoveNode(ha, pOldHash); pOldHash->dwName1 = 0xFFFFFFFF; pOldHash->dwName2 = 0xFFFFFFFF; pOldHash->lcLocale = 0xFFFF; pOldHash->wPlatform = 0xFFFF; pOldHash->dwBlockIndex = HASH_ENTRY_DELETED; // Note that this should always succeed; even if the hash table // was full, one entry was freed before. if((pNewHash = FindFreeHashEntry(ha, szNewFileName)) == NULL) nError = ERROR_CAN_NOT_COMPLETE; } // Save the block index and clear the hash entry if(nError == ERROR_SUCCESS) { // Copy the block table index pNewHash->dwBlockIndex = dwOldBlockIndex; pNewHash->lcLocale = (USHORT)lcSaveLocale; ha->dwFlags |= MPQ_FLAG_CHANGED; // Create new name node for the listfile nError = SListFileCreateNode(ha, szNewFileName, pNewHash); } // Resolve error and return 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); }