Esempio n. 1
0
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);
}
Esempio n. 2
0
bool WINAPI SFileSetMaxFileCount(HANDLE hMpq, DWORD dwMaxFileCount)
{
    TMPQArchive * ha = (TMPQArchive *)hMpq;
    DWORD dwNewHashTableSize = 0;
    int nError = ERROR_SUCCESS;

    // Test the valid parameters
    if(!IsValidMpqHandle(hMpq))
        nError = ERROR_INVALID_HANDLE;
    if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
        nError = ERROR_ACCESS_DENIED;
    if(dwMaxFileCount < ha->dwFileTableSize)
        nError = ERROR_DISK_FULL;

    // ALL file names must be known in order to be able
    // to rebuild hash table
    if(nError == ERROR_SUCCESS)
    {
        nError = CheckIfAllFilesKnown(ha, NULL, NULL);
    }

    // If the MPQ has a hash table, then we relocate the hash table
    if(nError == ERROR_SUCCESS)
    {
        // Calculate the hash table size for the new file limit
        dwNewHashTableSize = GetHashTableSizeForFileCount(dwMaxFileCount);

        // Rebuild both file tables
        nError = RebuildFileTable(ha, dwNewHashTableSize, dwMaxFileCount);
    }

    // We always have to rebuild the (attributes) file due to file table change
    if(nError == ERROR_SUCCESS)
    {
        // Invalidate (listfile) and (attributes)
        InvalidateInternalFiles(ha);

        // Rebuild the HET table, if we have any
        if(ha->pHetTable != NULL)
            nError = RebuildHetTable(ha);
    }

    // Return the error
    if(nError != ERROR_SUCCESS)
        SetLastError(nError);
    return (nError == ERROR_SUCCESS);
}
int EXPORT_SYMBOL SFileSetMaxFileCount(void * hMpq, uint32_t dwMaxFileCount)
{
    TMPQArchive * ha = (TMPQArchive *)hMpq;
    uint32_t dwNewHashTableSize = 0;
    int nError = ERROR_SUCCESS;

    /* Test the valid parameters */
    if(!IsValidMpqHandle(hMpq))
        nError = ERROR_INVALID_HANDLE;
    if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
        nError = ERROR_ACCESS_DENIED;
    if(dwMaxFileCount < ha->dwFileTableSize)
        nError = ERROR_DISK_FULL;

    /* ALL file names must be known in order to be able to rebuild hash table */
    if(nError == ERROR_SUCCESS && ha->pHashTable != NULL)
    {
        nError = CheckIfAllFilesKnown(ha);
        if(nError == ERROR_SUCCESS)
        {
            /* Calculate the hash table size for the new file limit */
            dwNewHashTableSize = GetHashTableSizeForFileCount(dwMaxFileCount);

            /* Rebuild both file tables */
            nError = RebuildFileTable(ha, dwNewHashTableSize);
        }
    }

    /* We always have to rebuild the (attributes) file due to file table change */
    if(nError == ERROR_SUCCESS)
    {
        /* Invalidate (listfile) and (attributes) */
        InvalidateInternalFiles(ha);

        /* Rebuild the HET table, if we have any */
        if(ha->pHetTable != NULL)
            nError = RebuildHetTable(ha);
    }

    /* Return the error */
    if(nError != ERROR_SUCCESS)
        SetLastError(nError);
    return (nError == ERROR_SUCCESS);
}
Esempio n. 4
0
bool WINAPI SFileCompactArchive(HANDLE hMpq, const char * szListFile, bool /* bReserved */)
{
    TFileStream * pTempStream = NULL;
    TMPQArchive * ha = (TMPQArchive *)hMpq;
    ULONGLONG ByteOffset;
    ULONGLONG ByteCount;
    LPDWORD pFileKeys = NULL;
    TCHAR szTempFile[MAX_PATH] = _T("");
    TCHAR * szTemp = NULL;
    int nError = ERROR_SUCCESS;

    // Test the valid parameters
    if(!IsValidMpqHandle(hMpq))
        nError = ERROR_INVALID_HANDLE;
    if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
        nError = ERROR_ACCESS_DENIED;

    // If the MPQ is changed at this moment, we have to flush the archive
    if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_CHANGED))
    {
        SFileFlushArchive(hMpq);
    }

    // Create the table with file keys
    if(nError == ERROR_SUCCESS)
    {
        if((pFileKeys = STORM_ALLOC(DWORD, ha->dwFileTableSize)) != NULL)
            memset(pFileKeys, 0, sizeof(DWORD) * ha->dwFileTableSize);
        else
            nError = ERROR_NOT_ENOUGH_MEMORY;
    }

    // First of all, we have to check of we are able to decrypt all files.
    // If not, sorry, but the archive cannot be compacted.
    if(nError == ERROR_SUCCESS)
    {
        // Initialize the progress variables for compact callback
        FileStream_GetSize(ha->pStream, &(ha->CompactTotalBytes));
        ha->CompactBytesProcessed = 0;
        nError = CheckIfAllFilesKnown(ha, szListFile, pFileKeys);
    }

    // Get the temporary file name and create it
    if(nError == ERROR_SUCCESS)
    {
        _tcscpy(szTempFile, FileStream_GetFileName(ha->pStream));
        if((szTemp = _tcsrchr(szTempFile, '.')) != NULL)
            _tcscpy(szTemp + 1, _T("mp_"));
        else
            _tcscat(szTempFile, _T("_"));

        pTempStream = FileStream_CreateFile(szTempFile, STREAM_PROVIDER_FLAT | BASE_PROVIDER_FILE);
        if(pTempStream == NULL)
            nError = GetLastError();
    }

    // Write the data before MPQ user data (if any)
    if(nError == ERROR_SUCCESS && ha->UserDataPos != 0)
    {
        // Inform the application about the progress
        if(ha->pfnCompactCB != NULL)
            ha->pfnCompactCB(ha->pvCompactUserData, CCB_COPYING_NON_MPQ_DATA, ha->CompactBytesProcessed, ha->CompactTotalBytes);

        ByteOffset = 0;
        ByteCount = ha->UserDataPos;
        nError = CopyNonMpqData(ha, ha->pStream, pTempStream, ByteOffset, ByteCount);
    }

    // Write the MPQ user data (if any)
    if(nError == ERROR_SUCCESS && ha->MpqPos > ha->UserDataPos)
    {
        // At this point, we assume that the user data size is equal
        // to pUserData->dwHeaderOffs.
        // If this assumption doesn't work, then we have an unknown version of MPQ
        ByteOffset = ha->UserDataPos;
        ByteCount = ha->MpqPos - ha->UserDataPos;

        assert(ha->pUserData != NULL);
        assert(ha->pUserData->dwHeaderOffs == ByteCount);
        nError = CopyNonMpqData(ha, ha->pStream, pTempStream, ByteOffset, ByteCount);
    }

    // Write the MPQ header
    if(nError == ERROR_SUCCESS)
    {
        TMPQHeader SaveMpqHeader;

        // Write the MPQ header to the file
        memcpy(&SaveMpqHeader, ha->pHeader, ha->pHeader->dwHeaderSize);
        BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_1);
        BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_2);
        BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_3);
        BSWAP_TMPQHEADER(&SaveMpqHeader, MPQ_FORMAT_VERSION_4);
        if(!FileStream_Write(pTempStream, NULL, &SaveMpqHeader, ha->pHeader->dwHeaderSize))
            nError = GetLastError();

        // Update the progress
        ha->CompactBytesProcessed += ha->pHeader->dwHeaderSize;
    }

    // Now copy all files
    if(nError == ERROR_SUCCESS)
        nError = CopyMpqFiles(ha, pFileKeys, pTempStream);

    // Defragment the file table
    if(nError == ERROR_SUCCESS)
        nError = RebuildFileTable(ha, ha->pHeader->dwHashTableSize, ha->dwMaxFileCount);

    // We also need to rebuild the HET table, if any
    if(nError == ERROR_SUCCESS)
    {
        // Invalidate (listfile) and (attributes)
        InvalidateInternalFiles(ha);

        // Rebuild the HET table, if we have any
        if(ha->pHetTable != NULL)
            nError = RebuildHetTable(ha);
    }

    // If succeeded, switch the streams
    if(nError == ERROR_SUCCESS)
    {
        if(FileStream_Replace(ha->pStream, pTempStream))
            pTempStream = NULL;
        else
            nError = ERROR_CAN_NOT_COMPLETE;
    }

    // If all succeeded, save the MPQ tables
    if(nError == ERROR_SUCCESS)
    {
        //
        // Note: We don't recalculate position of the MPQ tables at this point.
        // SaveMPQTables does it automatically.
        // 

        nError = SaveMPQTables(ha);
        if(nError == ERROR_SUCCESS && ha->pfnCompactCB != NULL)
        {
            ha->CompactBytesProcessed += (ha->pHeader->dwHashTableSize * sizeof(TMPQHash));
            ha->CompactBytesProcessed += (ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock));
            ha->pfnCompactCB(ha->pvCompactUserData, CCB_CLOSING_ARCHIVE, ha->CompactBytesProcessed, ha->CompactTotalBytes);
        }
    }

    // Cleanup and return
    if(pTempStream != NULL)
        FileStream_Close(pTempStream);
    if(pFileKeys != NULL)
        STORM_FREE(pFileKeys);
    if(nError != ERROR_SUCCESS)
        SetLastError(nError);
    return (nError == ERROR_SUCCESS);
}
bool WINAPI SFileSetMaxFileCount(HANDLE hMpq, DWORD dwMaxFileCount)
{
    TMPQHetTable * pOldHetTable = NULL;
    TMPQArchive * ha = (TMPQArchive *)hMpq;
    TFileEntry * pOldFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
    TFileEntry * pOldFileTable = NULL;
    TFileEntry * pOldFileEntry;
    TFileEntry * pFileEntry;
    TMPQHash * pOldHashTable = NULL;
    DWORD dwOldHashTableSize = 0;
    DWORD dwOldFileTableSize = 0;
    int nError = ERROR_SUCCESS;

    // Test the valid parameters
    if(!IsValidMpqHandle(ha))
        nError = ERROR_INVALID_HANDLE;
    if(ha->dwFlags & MPQ_FLAG_READ_ONLY)
        nError = ERROR_ACCESS_DENIED;

    // The new limit must not be lower than the index of the last file entry in the table
    if(nError == ERROR_SUCCESS && ha->dwFileTableSize > dwMaxFileCount)
        nError = ERROR_DISK_FULL;

    // ALL file names must be known in order to be able
    // to rebuild hash table size
    if(nError == ERROR_SUCCESS)
    {
        nError = CheckIfAllFilesKnown(ha, NULL, NULL);
    }

    // If the MPQ has a hash table, then we relocate the hash table
    if(nError == ERROR_SUCCESS && ha->pHashTable != NULL)
    {
        // Save parameters for the current hash table
        dwOldHashTableSize = ha->pHeader->dwHashTableSize;
        pOldHashTable = ha->pHashTable;

        // Allocate new hash table
        ha->pHeader->dwHashTableSize = GetHashTableSizeForFileCount(dwMaxFileCount);
        ha->pHashTable = STORM_ALLOC(TMPQHash, ha->pHeader->dwHashTableSize);
        if(ha->pHashTable != NULL)
            memset(ha->pHashTable, 0xFF, ha->pHeader->dwHashTableSize * sizeof(TMPQHash));
        else
            nError = ERROR_NOT_ENOUGH_MEMORY;
    }

    // If the MPQ has HET table, allocate new one as well
    if(nError == ERROR_SUCCESS && ha->pHetTable != NULL)
    {
        // Save the original HET table
        pOldHetTable = ha->pHetTable;

        // Create new one
        ha->pHetTable = CreateHetTable(dwMaxFileCount, 0x40, true);
        if(ha->pHetTable == NULL)
            nError = ERROR_NOT_ENOUGH_MEMORY;
    }

    // Now reallocate the file table
    if(nError == ERROR_SUCCESS)
    {
        // Save the current file table
        dwOldFileTableSize = ha->dwFileTableSize;
        pOldFileTable = ha->pFileTable;

        // Create new one
        ha->pFileTable = STORM_ALLOC(TFileEntry, dwMaxFileCount);
        if(ha->pFileTable != NULL)
            memset(ha->pFileTable, 0, dwMaxFileCount * sizeof(TFileEntry));
        else
            nError = ERROR_NOT_ENOUGH_MEMORY;
    }

    // Now we have to build both classic hash table and HET table.
    if(nError == ERROR_SUCCESS)
    {
        DWORD dwFileIndex = 0;
        DWORD dwHashIndex = 0;

        // Create new hash and HET entry for each file
        pFileEntry = ha->pFileTable;
        for(pOldFileEntry = pOldFileTable; pOldFileEntry < pOldFileTableEnd; pOldFileEntry++)
        {
            if(pOldFileEntry->dwFlags & MPQ_FILE_EXISTS)
            {
                // Copy the old file entry to the new one
                memcpy(pFileEntry, pOldFileEntry, sizeof(TFileEntry));
                assert(pFileEntry->szFileName != NULL);
                
                // Create new entry in the hash table
                if(ha->pHashTable != NULL)
                {
                    dwHashIndex = AllocateHashEntry(ha, pFileEntry);
                    if(dwHashIndex == HASH_ENTRY_FREE)
                    {
                        nError = ERROR_CAN_NOT_COMPLETE;
                        break;
                    }
                }

                // Create new entry in the HET table, if needed
                if(ha->pHetTable != NULL)
                {
                    dwHashIndex = AllocateHetEntry(ha, pFileEntry);
                    if(dwHashIndex == HASH_ENTRY_FREE)
                    {
                        nError = ERROR_CAN_NOT_COMPLETE;
                        break;
                    }
                }

                // Move to the next file entry in the new table
                pFileEntry++;
                dwFileIndex++;
            }
        }
    }

    // Mark the archive as changed
    // Note: We always have to rebuild the (attributes) file due to file table change
    if(nError == ERROR_SUCCESS)
    {
        ha->dwMaxFileCount = dwMaxFileCount;
        InvalidateInternalFiles(ha);
    }
    else
    {
        // Revert the hash table
        if(ha->pHashTable != NULL && pOldHashTable != NULL)
        {
            STORM_FREE(ha->pHashTable);
            ha->pHeader->dwHashTableSize = dwOldHashTableSize;
            ha->pHashTable = pOldHashTable;
        }

        // Revert the HET table
        if(ha->pHetTable != NULL && pOldHetTable != NULL)
        {
            FreeHetTable(ha->pHetTable);
            ha->pHetTable = pOldHetTable;
        }

        // Revert the file table
        if(pOldFileTable != NULL)
        {
            STORM_FREE(ha->pFileTable);
            ha->pFileTable = pOldFileTable;
        }

        SetLastError(nError);
    }

    // Return the result
    return (nError == ERROR_SUCCESS);
}
Esempio n. 6
0
int SFileAddFile_Init(
    TMPQArchive * ha,
    const char * szFileName,
    ULONGLONG FileTime,
    DWORD dwFileSize,
    LCID lcLocale,
    DWORD dwFlags,
    TMPQFile ** phf)
{
    TFileEntry * pFileEntry = NULL;
    TMPQFile * hf = NULL;               // File structure for newly added file
    DWORD dwHashIndex = HASH_ENTRY_FREE;
    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 = CreateWritableHandle(ha, dwFileSize);
    if(hf == NULL)
        return false;

    // Allocate file entry in the MPQ
    if(nError == ERROR_SUCCESS)
    {
        // Check if the file already exists in the archive
        pFileEntry = GetFileEntryExact(ha, szFileName, lcLocale, &dwHashIndex);
        if(pFileEntry != NULL)
        {
            if(dwFlags & MPQ_FILE_REPLACEEXISTING)
                InvalidateInternalFiles(ha);
            else
                nError = ERROR_ALREADY_EXISTS;
        }
        else
        {
            // Free all internal files - (listfile), (attributes), (signature)
            InvalidateInternalFiles(ha);
            
            // Attempt to allocate new file entry
            pFileEntry = AllocateFileEntry(ha, szFileName, lcLocale, &dwHashIndex);
            if(pFileEntry == NULL)
                nError = ERROR_DISK_FULL;   
        }

        // Set the file entry to the file structure
        hf->pFileEntry = pFileEntry;
    }

    // Prepare the pointer to hash table entry
    if(nError == ERROR_SUCCESS && ha->pHashTable != NULL && dwHashIndex < ha->pHeader->dwHashTableSize)
    {
        hf->pHashEntry = ha->pHashTable + dwHashIndex;
        hf->pHashEntry->lcLocale = (USHORT)lcLocale;
    }

    // Prepare the file key
    if(nError == ERROR_SUCCESS && (dwFlags & MPQ_FILE_ENCRYPTED))
    {
        hf->dwFileKey = DecryptFileKey(szFileName, hf->MpqFilePos, dwFileSize, dwFlags);
        if(hf->dwFileKey == 0)
            nError = ERROR_UNKNOWN_FILE_KEY;
    }

    // Fill the file entry and TMPQFile structure
    if(nError == ERROR_SUCCESS)
    {
        // At this point, the file name in the file entry must be set
        assert(pFileEntry->szFileName != NULL);
        assert(_stricmp(pFileEntry->szFileName, szFileName) == 0);

        nError = FillWritableHandle(ha, hf, FileTime, dwFileSize, dwFlags);
    }

    // Free the file handle if failed
    if(nError != ERROR_SUCCESS && hf != NULL)
        FreeFileHandle(hf);

    // Give the handle to the caller
    *phf = hf;
    return nError;
}
Esempio n. 7
0
// 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);
}