Beispiel #1
0
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;
        }
Beispiel #2
0
// 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;
}