Esempio n. 1
0
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;
}
Esempio n. 2
0
// 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
    DWORD dwSaveBlockIndex = 0;
    LCID lcSaveLocale = 0;
    int nError = ERROR_SUCCESS;

    // Test the valid parameters
    if(nError == ERROR_SUCCESS)
    {
        if(hMPQ == NULL || szNewFileName == NULL || *szNewFileName == 0)
            nError = ERROR_INVALID_PARAMETER;
        if(szFileName == NULL || *szFileName == 0)
            nError = ERROR_INVALID_PARAMETER;
    }

    // Do not allow to rename listfile
    if(nError == ERROR_SUCCESS)
    {
        if(!_stricmp(szFileName, LISTFILE_NAME))
            nError = ERROR_ACCESS_DENIED;
    }

    // Get the hash table entry for the original file
    if(nError == ERROR_SUCCESS)
    {
        if((pOldHash = GetHashEntryEx(ha, szFileName, lcLocale)) == NULL)
            nError = ERROR_FILE_NOT_FOUND;
    }

    // Test if the file already exists in the archive
    if(nError == ERROR_SUCCESS)
    {
        if((pNewHash = GetHashEntryEx(ha, szNewFileName, pOldHash->lcLocale)) != NULL)
            nError = ERROR_ALREADY_EXISTS;
    }

    // We have to know the decryption seed, otherwise we cannot re-crypt
    // the file after renaming
    if(nError == ERROR_SUCCESS)
    {
        // Save block table index and remove the hash table entry
        dwSaveBlockIndex = pOldHash->dwBlockIndex;
        lcSaveLocale = pOldHash->lcLocale;
        pBlock = ha->pBlockTable + dwSaveBlockIndex;

        // If the file is encrypted, we have to re-crypt the file content
        // with the new decryption seed
        if(pBlock->dwFlags & MPQ_FILE_ENCRYPTED)
        {
            nError = RecryptFileData(ha, dwSaveBlockIndex, szFileName, szNewFileName);
        }
    }

    // Get the hash table entry for the renamed file
    if(nError == ERROR_SUCCESS)
    {
        SListFileRemoveNode(ha, szFileName, lcSaveLocale);
        pOldHash->dwName1      = 0xFFFFFFFF;
        pOldHash->dwName2      = 0xFFFFFFFF;
        pOldHash->lcLocale     = 0xFFFF;
        pOldHash->wPlatform    = 0xFFFF;
        pOldHash->dwBlockIndex = HASH_ENTRY_DELETED;

        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 = dwSaveBlockIndex;
        pNewHash->lcLocale = (USHORT)lcSaveLocale;
        ha->dwFlags |= MPQ_FLAG_CHANGED;

        // Create new name node for the listfile
        nError = SListFileCreateNode(ha, szNewFileName, lcSaveLocale);
    }

    // Resolve error and return
    if(nError != ERROR_SUCCESS)
        SetLastError(nError);
    return (nError == ERROR_SUCCESS);
}
Esempio n. 3
0
// 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);
}
Esempio n. 4
0
// Renames the file within the archive.
// TODO: Test for archives > 4GB
BOOL WINAPI SFileRenameFile(HANDLE hMPQ, const char * szFileName, const char * szNewFileName)
{
    TMPQArchive * ha = (TMPQArchive *)hMPQ;
    TMPQHash * pOldHash = NULL;         // Hash entry for the original file
    TMPQHash * pNewHash = NULL;         // Hash entry for the renamed file
    DWORD dwBlockIndex = 0;
    int nError = ERROR_SUCCESS;

    // Test the valid parameters
    if(nError == ERROR_SUCCESS)
    {
        if(hMPQ == NULL || szNewFileName == NULL || *szNewFileName == 0)
            nError = ERROR_INVALID_PARAMETER;
        if(szFileName == NULL || *szFileName == 0)
            nError = ERROR_INVALID_PARAMETER;
    }

    // Do not allow to rename listfile
    if(nError == ERROR_SUCCESS)
    {
        if(!_stricmp(szFileName, LISTFILE_NAME))
            nError = ERROR_ACCESS_DENIED;
    }

    // Test if the file already exists in the archive
    if(nError == ERROR_SUCCESS)
    {
        if((pNewHash = GetHashEntryEx(ha, szNewFileName, lcLocale)) != NULL)
            nError = ERROR_ALREADY_EXISTS;
    }

    // Get the hash table entry for the original file
    if(nError == ERROR_SUCCESS)
    {
        if((pOldHash = GetHashEntryEx(ha, szFileName, lcLocale)) == NULL)
            nError = ERROR_FILE_NOT_FOUND;
    }

    // Get the hash table entry for the renamed file
    if(nError == ERROR_SUCCESS)
    {
        // Save block table index and remove the hash table entry
        dwBlockIndex = pOldHash->dwBlockIndex;
        pOldHash->dwName1      = 0xFFFFFFFF;
        pOldHash->dwName2      = 0xFFFFFFFF;
        pOldHash->lcLocale     = 0xFFFF;
        pOldHash->wPlatform    = 0xFFFF;
        pOldHash->dwBlockIndex = HASH_ENTRY_DELETED;

        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 = dwBlockIndex;
        ha->dwFlags |= MPQ_FLAG_CHANGED;
    }

    // Rename the file in the list file
    if(nError == ERROR_SUCCESS)
        nError = SListFileRenameNode(ha, szFileName, szNewFileName);

    // Resolve error and return
    if(nError != ERROR_SUCCESS)
        SetLastError(nError);
    return (nError == ERROR_SUCCESS);
}