static bool OpenLocalFile(const char * szFileName, HANDLE * phFile) { TFileStream * pStream; TMPQFile * hf = NULL; pStream = FileStream_OpenFile(szFileName, false); if (pStream != NULL) { // Allocate and initialize file handle hf = CreateMpqFile(NULL); if (hf != NULL) { hf->pStream = pStream; *phFile = hf; return true; } else { FileStream_Close(pStream); SetLastError(ERROR_NOT_ENOUGH_MEMORY); } } *phFile = NULL; return false; }
static bool OpenLocalFile(const char * szFileName, HANDLE * phFile) { TFileStream * pStream; TMPQFile * hf = NULL; TCHAR szFileNameT[MAX_PATH]; // Convert the file name to UNICODE (if needed) CopyFileName(szFileNameT, szFileName, strlen(szFileName)); // Open the file and create the TMPQFile structure pStream = FileStream_OpenFile(szFileNameT, STREAM_FLAG_READ_ONLY); if(pStream != NULL) { // Allocate and initialize file handle hf = CreateMpqFile(NULL); if(hf != NULL) { hf->pStream = pStream; *phFile = hf; return true; } else { FileStream_Close(pStream); SetLastError(ERROR_NOT_ENOUGH_MEMORY); } } *phFile = NULL; return false; }
int SFileAddFile_Init( TMPQArchive * ha, const char * szFileName, ULONGLONG FileTime, DWORD dwFileSize, LCID lcLocale, DWORD dwFlags, TMPQFile ** phf) { TFileEntry * pFileEntry = NULL; ULONGLONG TempPos; // For various file offset calculations TMPQFile * hf = NULL; // File structure for newly added file 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 // // Adjust file flags for too-small files if(dwFileSize < 0x04) dwFlags &= ~(MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY); if(dwFileSize < 0x20) dwFlags &= ~(MPQ_FILE_COMPRESSED | MPQ_FILE_SECTOR_CRC); // 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 = CreateMpqFile(ha); if(hf == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; // Find a free space in the MPQ, as well as free block table entry if(nError == ERROR_SUCCESS) { // Find the position where the file will be stored FindFreeMpqSpace(ha, &hf->MpqFilePos); hf->RawFilePos = ha->MpqPos + hf->MpqFilePos; hf->bIsWriteHandle = true; // When format V1, the size of the archive cannot exceed 4 GB if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) { TempPos = hf->MpqFilePos + dwFileSize; TempPos += ha->pHeader->dwHashTableSize * sizeof(TMPQHash); TempPos += ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock); TempPos += ha->pHeader->dwBlockTableSize * sizeof(USHORT); if((TempPos >> 32) != 0) nError = ERROR_DISK_FULL; }
static bool OpenLocalFile(const char * szFileName, HANDLE * phFile) { TFileStream * pStream; TMPQFile * hf = NULL; // We have to convert the local file name to UNICODE, if needed #ifdef _UNICODE TCHAR szFileNameT[MAX_PATH]; int i; for(i = 0; szFileName[i] != 0; i++) szFileNameT[i] = szFileName[i]; szFileNameT[i] = 0; pStream = FileStream_OpenFile(szFileNameT, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); #else pStream = FileStream_OpenFile(szFileName, STREAM_PROVIDER_LINEAR | BASE_PROVIDER_FILE); #endif if(pStream != NULL) { // Allocate and initialize file handle hf = CreateMpqFile(NULL); if(hf != NULL) { hf->pStream = pStream; *phFile = hf; return true; } else { FileStream_Close(pStream); SetLastError(ERROR_NOT_ENOUGH_MEMORY); } } *phFile = NULL; return false; }
static int CopyMpqFiles(TMPQArchive * ha, LPDWORD pFileKeys, TFileStream * pNewStream) { TFileEntry * pFileTableEnd = ha->pFileTable + ha->dwFileTableSize; TFileEntry * pFileEntry; TMPQFile * hf = NULL; 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) && pFileEntry->dwFileSize != 0) { // Allocate structure for the MPQ file hf = CreateMpqFile(ha); if (hf == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Store file entry hf->pFileEntry = pFileEntry; // Set the raw file position hf->MpqFilePos = pFileEntry->ByteOffset; hf->RawFilePos = ha->MpqPos + hf->MpqFilePos; // Set the file decryption key hf->dwFileKey = pFileKeys[pFileEntry - ha->pFileTable]; hf->dwDataSize = pFileEntry->dwFileSize; // 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); if (nError != ERROR_SUCCESS) break; // Free buffers. This also sets "hf" to NULL. FreeMPQFile(hf); } } // Cleanup and exit if (hf != NULL) FreeMPQFile(hf); return nError; }
int SFileAddFile_Init( TMPQArchive * ha, const char * szArchivedName, TMPQFileTime * pFT, DWORD dwFileSize, LCID lcLocale, DWORD dwFlags, TMPQFile ** phf) { LARGE_INTEGER TempPos; // For various file offset calculations TMPQFile * hf = NULL; // File structure for newly added file 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 // // Adjust file flags for too-small files if(dwFileSize < 0x04) dwFlags &= ~(MPQ_FILE_ENCRYPTED | MPQ_FILE_FIX_KEY); if(dwFileSize < 0x20) dwFlags &= ~(MPQ_FILE_COMPRESSED | MPQ_FILE_SECTOR_CRC); // Allocate the TMPQFile entry for newly added file hf = CreateMpqFile(ha, szArchivedName); if(hf == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; // If the MPQ header has not yet been written, do it now if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_NO_HEADER)) { // Remember the header size before swapping DWORD dwBytesToWrite = ha->pHeader->dwHeaderSize; BSWAP_TMPQHEADER(ha->pHeader); if(FileStream_Write(ha->pStream, &ha->MpqPos, ha->pHeader, dwBytesToWrite)) ha->dwFlags &= ~MPQ_FLAG_NO_HEADER; else nError = GetLastError(); BSWAP_TMPQHEADER(ha->pHeader); } if(nError == ERROR_SUCCESS) { // Check if the file already exists in the archive if((hf->pHash = GetHashEntryExact(ha, szArchivedName, lcLocale)) != NULL) { if(dwFlags & MPQ_FILE_REPLACEEXISTING) { hf->pBlockEx = ha->pExtBlockTable + hf->pHash->dwBlockIndex; hf->pBlock = ha->pBlockTable + hf->pHash->dwBlockIndex; } else { nError = ERROR_ALREADY_EXISTS; hf->pHash = NULL; } } if(nError == ERROR_SUCCESS && hf->pHash == NULL) { hf->pHash = FindFreeHashEntry(ha, szArchivedName); if(hf->pHash == NULL) { nError = ERROR_DISK_FULL; } } // Set the hash index hf->dwHashIndex = (DWORD)(hf->pHash - ha->pHashTable); hf->bIsWriteHandle = true; } // Find a free space in the MPQ, as well as free block table entry if(nError == ERROR_SUCCESS) { DWORD dwFreeBlock = FindFreeMpqSpace(ha, &hf->MpqFilePos); // Calculate the raw file offset hf->RawFilePos.QuadPart = ha->MpqPos.QuadPart + hf->MpqFilePos.QuadPart; // When format V1, the size of the archive cannot exceed 4 GB if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1) { TempPos.QuadPart = hf->MpqFilePos.QuadPart + dwFileSize; TempPos.QuadPart += ha->pHeader->dwHashTableSize * sizeof(TMPQHash); TempPos.QuadPart += ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock); TempPos.QuadPart += ha->pHeader->dwBlockTableSize * sizeof(TMPQBlockEx); if(TempPos.HighPart != 0) nError = ERROR_DISK_FULL; } // If we didn't get a block table entry assigned from hash table, assign it now if(hf->pBlock == NULL) { // Note: dwFreeBlock can be greater than dwHashTableSize, // in case that block table is bigger than hash table if(dwFreeBlock != 0xFFFFFFFF) { hf->pBlockEx = ha->pExtBlockTable + dwFreeBlock; hf->pBlock = ha->pBlockTable + dwFreeBlock; } else { nError = ERROR_DISK_FULL; } } // Calculate the index to the block table hf->dwBlockIndex = (DWORD)(hf->pBlock - ha->pBlockTable); } // Create key for file encryption if(nError == ERROR_SUCCESS && (dwFlags & MPQ_FILE_ENCRYPTED)) { szArchivedName = GetPlainMpqFileName(szArchivedName); hf->dwFileKey = DecryptFileKey(szArchivedName); if(dwFlags & MPQ_FILE_FIX_KEY) hf->dwFileKey = (hf->dwFileKey + hf->MpqFilePos.LowPart) ^ dwFileSize; } if(nError == ERROR_SUCCESS) { // Initialize the hash entry for the file hf->pHash->dwBlockIndex = hf->dwBlockIndex; hf->pHash->lcLocale = (USHORT)lcLocale; // Initialize the block table entry for the file hf->pBlockEx->wFilePosHigh = (USHORT)hf->MpqFilePos.HighPart; hf->pBlock->dwFilePos = hf->MpqFilePos.LowPart; hf->pBlock->dwFSize = dwFileSize; hf->pBlock->dwCSize = 0; hf->pBlock->dwFlags = dwFlags | MPQ_FILE_EXISTS; // Resolve CRC32 and MD5 entry for the file // Only do it when the MPQ archive has attributes if(ha->pAttributes != NULL) { hf->pFileTime = ha->pAttributes->pFileTime + hf->dwBlockIndex; hf->pCrc32 = ha->pAttributes->pCrc32 + hf->dwBlockIndex; hf->pMd5 = ha->pAttributes->pMd5 + hf->dwBlockIndex; // If the file has been overwritten, there still might be // stale entries in the attributes memset(hf->pFileTime, 0, sizeof(TMPQFileTime)); memset(hf->pMd5, 0, sizeof(TMPQMD5)); hf->pCrc32[0] = 0; // Initialize the file time, CRC32 and MD5 assert(sizeof(hf->hctx) >= sizeof(hash_state)); tommd5_init((hash_state *)hf->hctx); hf->dwCrc32 = crc32(0, Z_NULL, 0); // If the caller gave us a file time, use it. if(pFT != NULL) *hf->pFileTime = *pFT; } // Call the callback, if needed if(AddFileCB != NULL) AddFileCB(pvUserData, 0, hf->pBlock->dwFSize, false); } // If an error occured, remember it if(nError != ERROR_SUCCESS) hf->bErrorOccured = true; *phf = hf; return nError; }
// 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); }
static int CopyMpqFiles(TMPQArchive * ha, DWORD * pFileKeys, TFileStream * pNewStream) { TMPQBlockEx * pBlockEx = ha->pExtBlockTable; TMPQBlock * pBlockEnd = ha->pBlockTable + ha->pHeader->dwBlockTableSize; TMPQBlock * pBlock; TMPQFile * hf = NULL; int nError = ERROR_SUCCESS; // Walk through all files and write them to the destination MPQ archive for(pBlock = ha->pBlockTable; pBlock < pBlockEnd; pBlock++, pBlockEx++) { // Copy all the file sectors // Only do that when the file has nonzero size if((pBlock->dwFlags & MPQ_FILE_EXISTS) && pBlock->dwFSize != 0) { // Allocate structure for the MPQ file hf = CreateMpqFile(ha, "<compacting>"); if(hf == NULL) return ERROR_NOT_ENOUGH_MEMORY; // Store block positions hf->pBlockEx = pBlockEx; hf->pBlock = pBlock; // Set the raw file position hf->MpqFilePos.HighPart = hf->pBlockEx->wFilePosHigh; hf->MpqFilePos.LowPart = hf->pBlock->dwFilePos; hf->RawFilePos.QuadPart = ha->MpqPos.QuadPart + hf->MpqFilePos.LowPart; // Set the file decryption key hf->dwFileKey = pFileKeys[pBlock - ha->pBlockTable]; // 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; if(pBlock->dwFlags & MPQ_FILE_SECTOR_CRC) { nError = AllocateSectorChecksums(hf, false); if(nError != ERROR_SUCCESS) break; } // Copy all file sectors nError = CopyMpqFileSectors(ha, hf, pNewStream); if(nError != ERROR_SUCCESS) break; // Free buffers. This also sets "hf" to NULL. FreeMPQFile(hf); } } // Cleanup and exit if(hf != NULL) FreeMPQFile(hf); return nError; }