static bool GetDefaultPatchPrefix(
    const TCHAR * szBaseMpqName,
    char * szBuffer)
{
    const TCHAR * szExtension;
    const TCHAR * szDash;

    // Ensure that both names are plain names
    szBaseMpqName = GetPlainFileName(szBaseMpqName);

    // Patch prefix is for the Cataclysm MPQs, whose names
    // are like "locale-enGB.MPQ" or "speech-enGB.MPQ"
    szExtension = _tcsrchr(szBaseMpqName, _T('.'));
    szDash = _tcsrchr(szBaseMpqName, _T('-'));
    strcpy(szBuffer, "Base");

    // If the length of the prefix doesn't match, use default one
    if(szExtension != NULL && szDash != NULL && (szExtension - szDash) == 5)
    {
        // Copy the prefix
        szBuffer[0] = (char)szDash[1];
        szBuffer[1] = (char)szDash[2];
        szBuffer[2] = (char)szDash[3];
        szBuffer[3] = (char)szDash[4];
        szBuffer[4] = 0;
    }

    return true;
}
示例#2
0
static int ForceCreatePath(TCHAR * szFullPath)
{
    TCHAR * szPlainName = (TCHAR *)GetPlainFileName(szFullPath) - 1;
    TCHAR * szPathPart = szFullPath;
    TCHAR chSaveChar;

    // Skip disk drive and root directory
    if(szPathPart[0] != 0 && szPathPart[1] == _T(':'))
        szPathPart += 3;

    while(szPathPart <= szPlainName)
    {
        // If there is a delimiter, create the path fragment
        if(szPathPart[0] == _T('\\') || szPathPart[0] == _T('/'))
        {
            chSaveChar = szPathPart[0];
            szPathPart[0] = 0;

            CREATE_DIRECTORY(szFullPath);
            szPathPart[0] = chSaveChar;
        }

        // Move to the next character
        szPathPart++;
    }

    return ERROR_SUCCESS;
}
示例#3
0
static bool CalculateMpqHashSha1(TMPQArchive * ha, PMPQ_SIGNATURE_INFO pSI, unsigned char * sha1_tail0, unsigned char * sha1_tail1, unsigned char * sha1_tail2) {
    ULONGLONG BeginBuffer;
    hash_state sha1_state_temp;
    hash_state sha1_state;
    LPBYTE pbDigestBuffer = NULL;

    // Allocate buffer for creating the MPQ digest.
    pbDigestBuffer = ALLOCMEM(BYTE, MPQ_DIGEST_UNIT_SIZE);
    if (pbDigestBuffer == NULL)
        return false;

    // Initialize SHA1 state structure
    sha1_init(&sha1_state);

    // Calculate begin of data to be hashed
    BeginBuffer = pSI->BeginMpqData;

    // Create the digest
    for (;;) {
        ULONGLONG BytesRemaining;
        DWORD dwToRead = MPQ_DIGEST_UNIT_SIZE;

        // Check the number of bytes remaining
        BytesRemaining = pSI->EndMpqData - BeginBuffer;
        if (BytesRemaining < MPQ_DIGEST_UNIT_SIZE)
            dwToRead = (DWORD)BytesRemaining;
        if (dwToRead == 0)
            break;

        // Read the next chunk
        if (!FileStream_Read(ha->pStream, &BeginBuffer, pbDigestBuffer, dwToRead)) {
            FREEMEM(pbDigestBuffer);
            return false;
        }

        // Pass the buffer to the hashing function
        sha1_process(&sha1_state, pbDigestBuffer, dwToRead);

        // Move pointers
        BeginBuffer += dwToRead;
    }

    // Add all three known tails and generate three hashes
    memcpy(&sha1_state_temp, &sha1_state, sizeof(hash_state));
    sha1_done(&sha1_state_temp, sha1_tail0);

    memcpy(&sha1_state_temp, &sha1_state, sizeof(hash_state));
    AddTailToSha1(&sha1_state_temp, GetPlainFileName(ha->pStream->szFileName));
    sha1_done(&sha1_state_temp, sha1_tail1);

    memcpy(&sha1_state_temp, &sha1_state, sizeof(hash_state));
    AddTailToSha1(&sha1_state_temp, "ARCHIVE");
    sha1_done(&sha1_state_temp, sha1_tail2);

    // Finalize the MD5 hash
    FREEMEM(pbDigestBuffer);
    return true;
}
示例#4
0
static bool FindPatchPrefix_SC2(TMPQArchive * haBase, TMPQArchive * haPatch)
{
    TFileEntry * pFileTableEnd;
    TFileEntry * pFileEntry;
    TFileEntry * pBaseEntry;
    const char * szPlainName;
    char * szLstFileName;
    size_t cchWorkBuffer = 0x400;
    size_t cchBaseName;
    size_t cchDirName;
    bool bResult = false;

    // Find a *-md5.lst file in the base archive
    pBaseEntry = FindMd5ListFile(haBase);
    if(pBaseEntry == NULL)
        return false;
    cchBaseName = strlen(pBaseEntry->szFileName) + 1;

    // Allocate working buffer for merging LST file
    szLstFileName = STORM_ALLOC(char, cchWorkBuffer);
    if(szLstFileName != NULL)
    {
        // Find that file in the patch MPQ
        pFileTableEnd = haPatch->pFileTable + haPatch->dwFileTableSize;
        for(pFileEntry = haPatch->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
        {
            // Find the "(patch_metadata)" file within that folder
            // Note that the file is always relatively small and contains the patch prefix
            // Checking for file size greatly speeds up the search process
            if(pFileEntry->szFileName && !(pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) && (0 < pFileEntry->dwFileSize && pFileEntry->dwFileSize < 0x40))
            {
                // If the plain file name matches, we need to check its MD5
                szPlainName = GetPlainFileName(pFileEntry->szFileName);
                cchDirName = (size_t)(szPlainName - pFileEntry->szFileName);

                // The file name must not too long and must be PATCH_METADATA_NAME
                if((cchDirName + cchBaseName) < cchWorkBuffer && _stricmp(szPlainName, PATCH_METADATA_NAME) == 0)
                {
                    // Construct the name of the eventuall LST file
                    memcpy(szLstFileName, pFileEntry->szFileName, cchDirName);
                    memcpy(szLstFileName + cchDirName, pBaseEntry->szFileName, cchBaseName);

                    // If there is the "*-md5.lst" file in that directory, we check its MD5
                    if(IsMatchingPatchFile(haPatch, szLstFileName, pBaseEntry->md5))
                    {
                        bResult = CreatePatchPrefix(haPatch, pFileEntry->szFileName, szPlainName);
                        break;
                    }
                }
            }
        }

        // Free the work buffer
        STORM_FREE(szLstFileName);
    }

    return bResult;
}
示例#5
0
static void GetPlainAnsiFileName(
    const TCHAR * szFileName,
    char * szPlainName)
{
    const TCHAR * szPlainNameT = GetPlainFileName(szFileName);

    // Convert the plain name to ANSI
    while(*szPlainNameT != 0)
        *szPlainName++ = (char)*szPlainNameT++;
    *szPlainName = 0;
}
示例#6
0
static bool FindPatchPrefix_SC2(TMPQArchive * haBase, TMPQArchive * haPatch)
{
    TMPQNamePrefix * pPatchPrefix;
    TFileEntry * pBaseEntry;
    char * szLstFileName;
    char * szPlainName;
    size_t cchWorkBuffer = 0x400;
    bool bResult = false;

    // First-level patches: Find the same file within the patch archive
    // and verify by MD5-before-patch
    if(haBase->haPatch == NULL)
    {
        TFileEntry * pFileTableEnd = haPatch->pFileTable + haPatch->dwFileTableSize;
        TFileEntry * pFileEntry;

        // Allocate working buffer for merging LST file
        szLstFileName = STORM_ALLOC(char, cchWorkBuffer);
        if(szLstFileName != NULL)
        {
            // Find a *-md5.lst file in the base archive
            pBaseEntry = FindBaseLstFile(haBase);
            if(pBaseEntry == NULL)
            {
                STORM_FREE(szLstFileName);
                return false;
            }

            // Parse the entire file table
            for(pFileEntry = haPatch->pFileTable; pFileEntry < pFileTableEnd; pFileEntry++)
            {
                // Look for "patch_metadata" file
                if(IsPatchMetadataFile(pFileEntry))
                {
                    // Construct the name of the MD5 file
                    strcpy(szLstFileName, pFileEntry->szFileName);
                    szPlainName = (char *)GetPlainFileName(szLstFileName);
                    strcpy(szPlainName, pBaseEntry->szFileName);

                    // Check for matching MD5 file
                    if(IsMatchingPatchFile(haPatch, szLstFileName, pBaseEntry->md5))
                    {
                        bResult = CreatePatchPrefix(haPatch, szLstFileName, (size_t)(szPlainName - szLstFileName));
                        break;
                    }
                }
            }

            // Delete the merge buffer
            STORM_FREE(szLstFileName);
        }
    }
static inline bool IsPatchMetadataFile(TFileEntry * pFileEntry)
{
    // The file must ave a name
    if(pFileEntry->szFileName != NULL && (pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0)
    {
        // The file must be small
        if(0 < pFileEntry->dwFileSize && pFileEntry->dwFileSize < 0x40)
        {
            // Compare the plain name
            return (_stricmp(GetPlainFileName(pFileEntry->szFileName), PATCH_METADATA_NAME) == 0);
        }
    }

    // Not a patch_metadata
    return false;
}
static inline int IsPatchMetadataFile(TFileEntry * pFileEntry)
{
    /* The file must ave a namet */
    if(pFileEntry->szFileName != NULL && (pFileEntry->dwFlags & MPQ_FILE_PATCH_FILE) == 0)
    {
        /* The file must be small */
        if(0 < pFileEntry->dwFileSize && pFileEntry->dwFileSize < 0x40)
        {
            /* Compare the plain name */
            return (strcasecmp(GetPlainFileName(pFileEntry->szFileName), PATCH_METADATA_NAME) == 0);
        }
    }

    /* Not a patch_metadata */
    return 0;
}
示例#9
0
static bool DoMPQSearch_FileEntry(
    TMPQSearch * hs,
    SFILE_FIND_DATA * lpFindFileData,
    TMPQArchive * ha,
    TMPQHash * pHashEntry,
    TFileEntry * pFileEntry)
{
    TFileEntry * pPatchEntry;
    HANDLE hFile = NULL;
    const char * szFileName;
    size_t nPrefixLength = (ha->pPatchPrefix != NULL) ? ha->pPatchPrefix->nLength : 0;
    DWORD dwBlockIndex;
    char szNameBuff[MAX_PATH];

    // Is it a file but not a patch file?
    if((pFileEntry->dwFlags & hs->dwFlagMask) == MPQ_FILE_EXISTS)
    {
        // Now we have to check if this file was not enumerated before
        if(!FileWasFoundBefore(ha, hs, pFileEntry))
        {
//          if(pFileEntry != NULL && !_stricmp(pFileEntry->szFileName, "TriggerLibs\\NativeLib.galaxy"))
//              DebugBreak();

            // Find a patch to this file
            pPatchEntry = FindPatchEntry(ha, pFileEntry);
            if(pPatchEntry == NULL)
                pPatchEntry = pFileEntry;

            // Prepare the block index
            dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable);

            // Get the file name. If it's not known, we will create pseudo-name
            szFileName = pFileEntry->szFileName;
            if(szFileName == NULL)
            {
                // Open the file by its pseudo-name.
                sprintf(szNameBuff, "File%08u.xxx", (unsigned int)dwBlockIndex);
                if(SFileOpenFileEx((HANDLE)hs->ha, szNameBuff, SFILE_OPEN_BASE_FILE, &hFile))
                {
                    SFileGetFileName(hFile, szNameBuff);
                    szFileName = szNameBuff;
                    SFileCloseFile(hFile);
                }
            }

            // If the file name is still NULL, we cannot include the file to search results
            if(szFileName != NULL)
            {
                // Check the file name against the wildcard
                if(CheckWildCard(szFileName + nPrefixLength, hs->szSearchMask))
                {
                    // Fill the found entry. hash entry and block index are taken from the base MPQ
                    lpFindFileData->dwHashIndex  = HASH_ENTRY_FREE;
                    lpFindFileData->dwBlockIndex = dwBlockIndex;
                    lpFindFileData->dwFileSize   = pPatchEntry->dwFileSize;
                    lpFindFileData->dwFileFlags  = pPatchEntry->dwFlags;
                    lpFindFileData->dwCompSize   = pPatchEntry->dwCmpSize;
                    lpFindFileData->lcLocale     = 0;   // pPatchEntry->lcLocale;

                    // Fill the filetime
                    lpFindFileData->dwFileTimeHi = (DWORD)(pPatchEntry->FileTime >> 32);
                    lpFindFileData->dwFileTimeLo = (DWORD)(pPatchEntry->FileTime);

                    // Fill-in the entries from hash table entry, if given
                    if(pHashEntry != NULL)
                    {
                        lpFindFileData->dwHashIndex = (DWORD)(pHashEntry - ha->pHashTable);
                        lpFindFileData->lcLocale = pHashEntry->lcLocale;
                    }

                    // Fill the file name and plain file name
                    StringCopyA(lpFindFileData->cFileName, szFileName + nPrefixLength, MAX_PATH-1);
                    lpFindFileData->szPlainName = (char *)GetPlainFileName(lpFindFileData->cFileName);
                    return true;
                }
            }
        }
示例#10
0
// Performs one MPQ search
static int DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData)
{
    TMPQArchive * ha = hs->ha;
    TFileEntry * pFileTableEnd;
    TFileEntry * pPatchEntry;
    TFileEntry * pFileEntry;
    const char * szFileName;
    HANDLE hFile;
    char szPseudoName[20];
    DWORD dwBlockIndex;
    size_t nPrefixLength;

    // Start searching with base MPQ
    while(ha != NULL)
    {
        // Now parse the file entry table in order to get all files.
        pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
        pFileEntry = ha->pFileTable + hs->dwNextIndex;

        // Get the length of the patch prefix (0 if none)
        nPrefixLength = (ha->pPatchPrefix != NULL) ? ha->pPatchPrefix->nLength : 0;

        // Parse the file table
        while(pFileEntry < pFileTableEnd)
        {
            // Increment the next index for subsequent search
            hs->dwNextIndex++;

            // Is it a file but not a patch file?
            if((pFileEntry->dwFlags & hs->dwFlagMask) == MPQ_FILE_EXISTS)
            {
                // Spazzler3 protector: Some files are clearly wrong
                if(!FileEntryIsInvalid(ha, pFileEntry))
                {                    
                    // Now we have to check if this file was not enumerated before
                    if(!FileWasFoundBefore(ha, hs, pFileEntry))
                    {
//                      if(pFileEntry != NULL && !_stricmp(pFileEntry->szFileName, "TriggerLibs\\NativeLib.galaxy"))
//                          DebugBreak();

                        // Find a patch to this file
                        pPatchEntry = FindPatchEntry(ha, pFileEntry);
                        if(pPatchEntry == NULL)
                            pPatchEntry = pFileEntry;

                        // Prepare the block index
                        dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable);

                        // Get the file name. If it's not known, we will create pseudo-name
                        szFileName = pFileEntry->szFileName;
                        if(szFileName == NULL)
                        {
                            // Open the file by its pseudo-name.
                            // This also generates the file name with a proper extension
                            sprintf(szPseudoName, "File%08u.xxx", (unsigned int)dwBlockIndex);
                            if(SFileOpenFileEx((HANDLE)hs->ha, szPseudoName, SFILE_OPEN_BASE_FILE, &hFile))
                            {
                                szFileName = (pFileEntry->szFileName != NULL) ? pFileEntry->szFileName : szPseudoName;
                                SFileCloseFile(hFile);
                            }
                        }

                        // If the file name is still NULL, we cannot include the file to search results
                        if(szFileName != NULL)
                        {
                            // Check the file name against the wildcard
                            if(CheckWildCard(szFileName + nPrefixLength, hs->szSearchMask))
                            {
                                // Fill the found entry. hash entry and block index are taken from the base MPQ
                                lpFindFileData->dwHashIndex  = pFileEntry->dwHashIndex;
                                lpFindFileData->dwBlockIndex = dwBlockIndex;
                                lpFindFileData->dwFileSize   = pPatchEntry->dwFileSize;
                                lpFindFileData->dwFileFlags  = pPatchEntry->dwFlags;
                                lpFindFileData->dwCompSize   = pPatchEntry->dwCmpSize;
                                lpFindFileData->lcLocale     = pPatchEntry->lcLocale;

                                // Fill the filetime
                                lpFindFileData->dwFileTimeHi = (DWORD)(pPatchEntry->FileTime >> 32);
                                lpFindFileData->dwFileTimeLo = (DWORD)(pPatchEntry->FileTime);

                                // Fill the file name and plain file name
                                strcpy(lpFindFileData->cFileName, szFileName + nPrefixLength);
                                lpFindFileData->szPlainName = (char *)GetPlainFileName(lpFindFileData->cFileName);
                                return ERROR_SUCCESS;
                            }
                        }
                    }
                }
            }

            pFileEntry++;
        }
示例#11
0
// Performs one MPQ search
static int DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData)
{
    TMPQArchive * ha = hs->ha;
    TFileEntry * pFileTableEnd;
    TFileEntry * pPatchEntry;
    TFileEntry * pFileEntry;
    const char * szFileName;
    HANDLE hFile;
    char szPseudoName[20];
    DWORD dwBlockIndex;
    size_t nPrefixLength;

    // Start searching with base MPQ
    while(ha != NULL)
    {
        // Now parse the file entry table in order to get all files.
        pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
        pFileEntry = ha->pFileTable + hs->dwNextIndex;

        // Get the start and end of the hash table
        nPrefixLength = strlen(ha->szPatchPrefix);

        // Parse the file table
        while(pFileEntry < pFileTableEnd)
        {
            // Increment the next index for subsequent search
            hs->dwNextIndex++;

            // Is it a file and not a patch file?
            if((pFileEntry->dwFlags & hs->dwFlagMask) == MPQ_FILE_EXISTS)
            {
                // Now we have to check if this file was not enumerated before
                if(!FileWasFoundBefore(ha, hs, pFileEntry))
                {
                    // Find a patch to this file
                    pPatchEntry = FindPatchEntry(ha, pFileEntry);
                    if(pPatchEntry == NULL)
                        pPatchEntry = pFileEntry;

                    // Prepare the block index
                    dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable);

                    // Get the file name. If it's not known, we will create pseudo-name
                    szFileName = pFileEntry->szFileName;
                    if(szFileName == NULL)
                    {
                        // Open the file by index in order to check if the file exists
                        if(SFileOpenFileEx((HANDLE)hs->ha, (char *)(DWORD_PTR)dwBlockIndex, SFILE_OPEN_BY_INDEX, &hFile))
                            SFileCloseFile(hFile);

                        // If the name was retrieved, use that one. Otherwise, just use generic pseudo-name
                        szFileName = pFileEntry->szFileName;
                        if(szFileName == NULL)
                        {
                            sprintf(szPseudoName, "File%08u.xxx", dwBlockIndex);
                            szFileName = szPseudoName;
                        }
                    }

                    // Check the file name against the wildcard
                    if(CheckWildCard(szFileName, hs->szSearchMask))
                    {
                        // Fill the found entry
                        lpFindFileData->dwHashIndex  = pPatchEntry->dwHashIndex;
                        lpFindFileData->dwBlockIndex = dwBlockIndex;
                        lpFindFileData->dwFileSize   = pPatchEntry->dwFileSize;
                        lpFindFileData->dwFileFlags  = pPatchEntry->dwFlags;
                        lpFindFileData->dwCompSize   = pPatchEntry->dwCmpSize;
                        lpFindFileData->lcLocale     = pPatchEntry->lcLocale;

                        // Fill the filetime
                        lpFindFileData->dwFileTimeHi = (DWORD)(pPatchEntry->FileTime >> 32);
                        lpFindFileData->dwFileTimeLo = (DWORD)(pPatchEntry->FileTime);

                        // Fill the file name and plain file name
                        strcpy(lpFindFileData->cFileName, szFileName + nPrefixLength);
                        lpFindFileData->szPlainName = (char *)GetPlainFileName(lpFindFileData->cFileName);
                        return ERROR_SUCCESS;
                    }

                }
            }

            pFileEntry++;
        }
示例#12
0
static int RecryptFileData(
    TMPQArchive * ha,
    TMPQFile * hf,
    const char * szFileName,
    const char * szNewFileName)
{
    ULONGLONG RawFilePos;
    TFileEntry * pFileEntry = hf->pFileEntry;
    DWORD dwBytesToRecrypt = pFileEntry->dwCmpSize;
    DWORD dwOldKey;
    DWORD dwNewKey;
    int nError = ERROR_SUCCESS;

    // The file must be encrypted
    assert(pFileEntry->dwFlags & MPQ_FILE_ENCRYPTED);

    // File decryption key is calculated from the plain name
    szNewFileName = GetPlainFileName(szNewFileName);
    szFileName = GetPlainFileName(szFileName);

    // Calculate both file keys
    dwOldKey = DecryptFileKey(szFileName,    pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags);
    dwNewKey = DecryptFileKey(szNewFileName, pFileEntry->ByteOffset, pFileEntry->dwFileSize, pFileEntry->dwFlags);

    // Incase the keys are equal, don't recrypt the file
    if(dwNewKey == dwOldKey)
        return ERROR_SUCCESS;
    hf->dwFileKey = dwOldKey;

    // Calculate the raw position of the file in the archive
    hf->MpqFilePos = pFileEntry->ByteOffset;
    hf->RawFilePos = ha->MpqPos + hf->MpqFilePos;

    // Allocate buffer for file transfer
    nError = AllocateSectorBuffer(hf);
    if(nError != ERROR_SUCCESS)
        return nError;

    // Also allocate buffer for sector offsets
    // Note: Don't load sector checksums, we don't need to recrypt them
    nError = AllocateSectorOffsets(hf, true);
    if(nError != ERROR_SUCCESS)
        return nError;

    // If we have sector offsets, recrypt these as well
    if(hf->SectorOffsets != NULL)
    {
        // Allocate secondary buffer for sectors copy
        DWORD * SectorOffsetsCopy = STORM_ALLOC(DWORD, hf->SectorOffsets[0] / sizeof(DWORD));
        DWORD dwSectorOffsLen = hf->SectorOffsets[0];

        if(SectorOffsetsCopy == NULL)
            return ERROR_NOT_ENOUGH_MEMORY;

        // Recrypt the array of sector offsets
        memcpy(SectorOffsetsCopy, hf->SectorOffsets, dwSectorOffsLen);
        EncryptMpqBlock(SectorOffsetsCopy, dwSectorOffsLen, dwNewKey - 1);
        BSWAP_ARRAY32_UNSIGNED(SectorOffsetsCopy, dwSectorOffsLen);

        // Write the recrypted array back
        if(!FileStream_Write(ha->pStream, &hf->RawFilePos, SectorOffsetsCopy, dwSectorOffsLen))
            nError = GetLastError();
        STORM_FREE(SectorOffsetsCopy);
    }

    // Now we have to recrypt all file sectors. We do it without
    // recompression, because recompression is not necessary in this case
    if(nError == ERROR_SUCCESS)
    {
        for(DWORD dwSector = 0; dwSector < hf->dwSectorCount; dwSector++)
        {
            DWORD dwRawDataInSector = hf->dwSectorSize;
            DWORD dwRawByteOffset = dwSector * hf->dwSectorSize;

            // Last sector: If there is not enough bytes remaining in the file, cut the raw size
            if(dwRawDataInSector > dwBytesToRecrypt)
                dwRawDataInSector = dwBytesToRecrypt;

            // Fix the raw data length if the file is compressed
            if(hf->SectorOffsets != NULL)
            {
                dwRawDataInSector = hf->SectorOffsets[dwSector+1] - hf->SectorOffsets[dwSector];
                dwRawByteOffset = hf->SectorOffsets[dwSector];
            }

            // Calculate the raw file offset of the file sector
            RawFilePos = CalculateRawSectorOffset(hf, dwRawByteOffset);

            // Read the file sector
            if(!FileStream_Read(ha->pStream, &RawFilePos, hf->pbFileSector, dwRawDataInSector))
            {
                nError = GetLastError();
                break;
            }

            // If necessary, re-encrypt the sector
            // Note: Recompression is not necessary here. Unlike encryption, 
            // the compression does not depend on the position of the file in MPQ.
            BSWAP_ARRAY32_UNSIGNED(hf->pbFileSector, dwRawDataInSector);
            DecryptMpqBlock(hf->pbFileSector, dwRawDataInSector, dwOldKey + dwSector);
            EncryptMpqBlock(hf->pbFileSector, dwRawDataInSector, dwNewKey + dwSector);
            BSWAP_ARRAY32_UNSIGNED(hf->pbFileSector, dwRawDataInSector);

            // Write the sector back
            if(!FileStream_Write(ha->pStream, &RawFilePos, hf->pbFileSector, dwRawDataInSector))
            {
                nError = GetLastError();
                break;
            }

            // Decrement number of bytes remaining
            dwBytesToRecrypt -= hf->dwSectorSize;
        }
    }

    return nError;
}
示例#13
0
// Performs one MPQ search
static int DoMPQSearch(TMPQSearch * hs, SFILE_FIND_DATA * lpFindFileData)
{
    TMPQArchive * ha = hs->ha;
    TFileEntry * pFileTableEnd;
    TFileEntry * pFileEntry;
    const char * szFileName;
    char szPseudoName[20];
    DWORD dwBlockIndex;
    size_t nPrefixLength;

    // Do that for all files in the patch chain
    while(ha != NULL)
    {
        // Now parse the file entry table in order to get all files.
        pFileTableEnd = ha->pFileTable + ha->dwFileTableSize;
        pFileEntry = ha->pFileTable + hs->dwNextIndex;

        // Get the start and end of the hash table
        nPrefixLength = strlen(ha->szPatchPrefix);

        // Parse the file table
        while(pFileEntry < pFileTableEnd)
        {
            // Increment the next index for subsequent search
            hs->dwNextIndex++;

            // Does the block exist ?
            if(pFileEntry->dwFlags & MPQ_FILE_EXISTS)
            {
                // Prepare the block index
                dwBlockIndex = (DWORD)(pFileEntry - ha->pFileTable);

                // Get the file name. If it's not known, we will create pseudo-name
                szFileName = pFileEntry->szFileName;
                if(szFileName == NULL)
                {
                    HANDLE hFile;

                    // Open the file by index in order to check if the file exists
                    if(SFileOpenFileEx((HANDLE)hs->ha, (char *)(DWORD_PTR)dwBlockIndex, SFILE_OPEN_BY_INDEX, &hFile))
                        SFileCloseFile(hFile);

                    // If the name was retrieved, use that one. Otherwise, just use generic pseudo-name
                    szFileName = pFileEntry->szFileName;
                    if(szFileName == NULL)
                    {
                        sprintf(szPseudoName, "File%08u.xxx", dwBlockIndex);
                        szFileName = szPseudoName;
                    }
                }

                // If we are already in the patch MPQ, we skip all files
                // that don't have the appropriate patch prefix and are patch files
                if(ha->haBase != NULL)
                {
                    // If the file has different patch prefix, don't report it
                    if(nPrefixLength != 0 && _strnicmp(szFileName, ha->szPatchPrefix, nPrefixLength))
                        goto __SkipThisFile;

                    //
                    // We need to properly handle the following case:
                    //
                    // 1) Base MPQ file doesn't contain the desired file
                    // 2) First patch MPQ contains the file with MPQ_FILE_PATCH_FILE
                    // 3) Second patch contains full version of the file (MPQ_FILE_PATCH_FILE is not set)
                    //

                    if(IsBaseFileMissing(ha, szFileName, szFileName + nPrefixLength, pFileEntry->lcLocale))
                        goto __SkipThisFile;
                }

                // Check the file name.
                if(CheckWildCard(szFileName, hs->szSearchMask))
                {
                    // Fill the found entry
                    lpFindFileData->dwHashIndex  = pFileEntry->dwHashIndex;
                    lpFindFileData->dwBlockIndex = dwBlockIndex;
                    lpFindFileData->dwFileSize   = pFileEntry->dwFileSize;
                    lpFindFileData->dwFileFlags  = pFileEntry->dwFlags;
                    lpFindFileData->dwCompSize   = pFileEntry->dwCmpSize;
                    lpFindFileData->lcLocale     = pFileEntry->lcLocale;

                    // Fill the filetime
                    lpFindFileData->dwFileTimeHi = (DWORD)(pFileEntry->FileTime >> 32);
                    lpFindFileData->dwFileTimeLo = (DWORD)(pFileEntry->FileTime);

                    // Fill the file name and plain file name
                    strcpy(lpFindFileData->cFileName, szFileName + nPrefixLength);
                    lpFindFileData->szPlainName = (char *)GetPlainFileName(lpFindFileData->cFileName);
                    return ERROR_SUCCESS;
                }
            }

            // Move to the next file entry
__SkipThisFile:

            pFileEntry++;
        }