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 // // 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 = CreateFileHandle(ha, NULL); if(hf == NULL) nError = ERROR_NOT_ENOUGH_MEMORY; // Find a free space in the MPQ and verify if it's not over 4 GB on MPQs v1 if(nError == ERROR_SUCCESS) { // Find the position where the file will be stored hf->MpqFilePos = FindFreeMpqSpace(ha); 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; }
// Calculate begin and end of the MPQ archive static void CalculateArchiveRange( TMPQArchive * ha, PMPQ_SIGNATURE_INFO pSI) { TMPQHeader * pHeader = ha->pHeader; ULONGLONG TempPos = 0; ULONGLONG MaxPos; char szMapHeader[0x200]; // Get the MPQ begin pSI->BeginMpqData = ha->MpqPos; // Warcraft III maps are signed from the map header to the end if(FileStream_Read(ha->pStream, &TempPos, szMapHeader, sizeof(szMapHeader))) { // Is it a map header ? if(szMapHeader[0] == 'H' && szMapHeader[1] == 'M' && szMapHeader[2] == '3' && szMapHeader[3] == 'W') { // We will have to hash since the map header pSI->BeginMpqData = 0; } } // Get the MPQ data end. The end is calculated as the biggest // value of (end of the last file), (end of block table), // (end of ext block table), (end of hash table) FindFreeMpqSpace(ha, &MaxPos); // Check if hash table is beyond TempPos = ha->MpqPos + MAKE_OFFSET64(pHeader->wHashTablePosHi, pHeader->dwHashTablePos) + pHeader->HashTableSize64; if(TempPos > MaxPos) MaxPos = TempPos; // Check if block table is beyond TempPos = ha->MpqPos + MAKE_OFFSET64(pHeader->wBlockTablePosHi, pHeader->dwBlockTablePos) + pHeader->BlockTableSize64; if(TempPos > MaxPos) MaxPos = TempPos; // Check if ext block table is beyond if(pHeader->HiBlockTablePos64 != 0) { TempPos = ha->MpqPos + pHeader->HiBlockTablePos64 + pHeader->HiBlockTableSize64; if(TempPos > MaxPos) MaxPos = TempPos; } // Give the end pSI->EndMpqData = MaxPos; // Get the size of the entire file FileStream_GetSize(ha->pStream, pSI->EndOfFile); }
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; }