📄 sfileopenarchive.cpp
字号:
break;
}
// Move to the next possible offset
SearchPos.QuadPart += 0x200;
MpqPos = SearchPos;
}
}
// Relocate tables position
if(nError == ERROR_SUCCESS)
{
// Clear the fields not supported in older formats
if(ha->pHeader->wFormatVersion < MPQ_FORMAT_VERSION_2)
{
ha->pHeader->ExtBlockTablePos.QuadPart = 0;
ha->pHeader->wBlockTablePosHigh = 0;
ha->pHeader->wHashTablePosHigh = 0;
}
ha->dwBlockSize = (0x200 << ha->pHeader->wBlockSize);
nError = RelocateMpqTablePositions(ha);
}
// Allocate buffers
if(nError == ERROR_SUCCESS)
{
//
// Note that the block table should be as large as the hash table
// (For later file additions).
//
// I have found a MPQ which has the block table larger than
// the hash table. We should avoid buffer overruns caused by that.
//
dwBlockTableSize = max(ha->pHeader->dwHashTableSize, ha->pHeader->dwBlockTableSize);
ha->pHashTable = ALLOCMEM(TMPQHash, ha->pHeader->dwHashTableSize);
ha->pBlockTable = ALLOCMEM(TMPQBlock, dwBlockTableSize);
ha->pExtBlockTable = ALLOCMEM(TMPQBlockEx, dwBlockTableSize);
ha->pbBlockBuffer = ALLOCMEM(BYTE, ha->dwBlockSize);
if(!ha->pHashTable || !ha->pBlockTable || !ha->pExtBlockTable || !ha->pbBlockBuffer)
nError = ERROR_NOT_ENOUGH_MEMORY;
}
// Read the hash table into memory
if(nError == ERROR_SUCCESS)
{
dwBytes = ha->pHeader->dwHashTableSize * sizeof(TMPQHash);
SetFilePointer(ha->hFile, ha->HashTablePos.LowPart, &ha->HashTablePos.HighPart, FILE_BEGIN);
ReadFile(ha->hFile, ha->pHashTable, dwBytes, &dwTransferred, NULL);
if(dwTransferred != dwBytes)
nError = ERROR_FILE_CORRUPT;
}
// Decrypt hash table and check if it is correctly decrypted
if(nError == ERROR_SUCCESS)
{
TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize;
TMPQHash * pHash;
// We have to convert the hash table from LittleEndian
BSWAP_ARRAY32_UNSIGNED((DWORD *)ha->pHashTable, (dwBytes / sizeof(DWORD)));
DecryptHashTable((DWORD *)ha->pHashTable, (BYTE *)"(hash table)", (ha->pHeader->dwHashTableSize * 4));
// Check hash table if is correctly decrypted
for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++)
{
// Note: Some MPQs from World of Warcraft have wPlatform set to 0x0100.
// If not free or deleted hash entry, check for valid values
if(pHash->dwBlockIndex < HASH_ENTRY_DELETED)
{
// The block index should not be larger than size of the block table
if(pHash->dwBlockIndex > ha->pHeader->dwBlockTableSize)
{
nError = ERROR_BAD_FORMAT;
break;
}
// Remember the highest block table entry
if(pHash->dwBlockIndex > dwMaxBlockIndex)
dwMaxBlockIndex = pHash->dwBlockIndex;
}
}
}
// Now, read the block table
if(nError == ERROR_SUCCESS)
{
memset(ha->pBlockTable, 0, dwBlockTableSize * sizeof(TMPQBlock));
dwBytes = ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock);
SetFilePointer(ha->hFile, ha->BlockTablePos.LowPart, &ha->BlockTablePos.HighPart, FILE_BEGIN);
ReadFile(ha->hFile, ha->pBlockTable, dwBytes, &dwTransferred, NULL);
// We have to convert every DWORD in ha->block from LittleEndian
BSWAP_ARRAY32_UNSIGNED((DWORD *)ha->pBlockTable, dwBytes / sizeof(DWORD));
if(dwTransferred != dwBytes)
nError = ERROR_FILE_CORRUPT;
}
// Decrypt block table.
// Some MPQs don't have Decrypted block table, e.g. cracked Diablo version
// We have to check if block table is really encrypted
if(nError == ERROR_SUCCESS)
{
TMPQBlock * pBlockEnd = ha->pBlockTable + ha->pHeader->dwBlockTableSize;
TMPQBlock * pBlock = ha->pBlockTable;
BOOL bBlockTableEncrypted = FALSE;
// Verify all blocks entries in the table
// The loop usually stops at the first entry
while(pBlock < pBlockEnd)
{
// The lower 8 bits of the MPQ flags are always zero.
// Note that this may change in next MPQ versions
if(pBlock->dwFlags & 0x000000FF)
{
bBlockTableEncrypted = TRUE;
break;
}
// Move to the next block table entry
pBlock++;
}
if(bBlockTableEncrypted)
{
DecryptBlockTable((DWORD *)ha->pBlockTable,
(BYTE *)"(block table)",
(ha->pHeader->dwBlockTableSize * 4));
}
}
// Now, read the extended block table.
// For V1 archives, we still will maintain the extended block table
// (it will be filled with zeros)
// TODO: Test with >4GB
if(nError == ERROR_SUCCESS)
{
memset(ha->pExtBlockTable, 0, dwBlockTableSize * sizeof(TMPQBlockEx));
if(ha->pHeader->ExtBlockTablePos.QuadPart != 0)
{
dwBytes = ha->pHeader->dwBlockTableSize * sizeof(TMPQBlockEx);
SetFilePointer(ha->hFile,
ha->ExtBlockTablePos.LowPart,
&ha->ExtBlockTablePos.HighPart,
FILE_BEGIN);
ReadFile(ha->hFile, ha->pExtBlockTable, dwBytes, &dwTransferred, NULL);
// We have to convert every DWORD in ha->block from LittleEndian
BSWAP_ARRAY16_UNSIGNED((USHORT *)ha->pExtBlockTable, dwBytes / sizeof(USHORT));
// The extended block table is not encrypted (so far)
if(dwTransferred != dwBytes)
nError = ERROR_FILE_CORRUPT;
}
}
// Verify the both block tables (If the MPQ file is not protected)
if(nError == ERROR_SUCCESS && (ha->dwFlags & MPQ_FLAG_PROTECTED) == 0)
{
TMPQBlockEx * pBlockEx = ha->pExtBlockTable;
TMPQBlock * pBlockEnd = ha->pBlockTable + dwMaxBlockIndex + 1;
TMPQBlock * pBlock = ha->pBlockTable;
// If the MPQ file is not protected,
// we will check if all sizes in the block table is correct.
// Note that we will not relocate the block table (change from previous versions)
for(; pBlock < pBlockEnd; pBlock++, pBlockEx++)
{
if(pBlock->dwFlags & MPQ_FILE_EXISTS)
{
// Get the 64-bit file position
TempPos.HighPart = pBlockEx->wFilePosHigh;
TempPos.LowPart = pBlock->dwFilePos;
if(TempPos.QuadPart > ha->MpqSize.QuadPart || pBlock->dwCSize > ha->MpqSize.QuadPart)
{
nError = ERROR_BAD_FORMAT;
break;
}
}
}
}
// If the user didn't specified otherwise,
// include the internal listfile to the TMPQArchive structure
if((dwFlags & MPQ_OPEN_NO_LISTFILE) == 0)
{
if(nError == ERROR_SUCCESS)
SListFileCreateListFile(ha);
// Add the internal listfile
if(nError == ERROR_SUCCESS)
SFileAddListFile((HANDLE)ha, NULL);
}
// Cleanup and exit
if(nError != ERROR_SUCCESS)
{
FreeMPQArchive(ha);
if(hFile != INVALID_HANDLE_VALUE)
CloseHandle(hFile);
SetLastError(nError);
}
else
{
if(pFirstOpen == NULL)
pFirstOpen = ha;
}
*phMPQ = ha;
return (nError == ERROR_SUCCESS);
}
BOOL WINAPI SFileOpenArchive(const char * szMpqName, DWORD dwPriority, DWORD dwFlags, HANDLE * phMPQ)
{
return SFileOpenArchiveEx(szMpqName, dwPriority, dwFlags, phMPQ, GENERIC_READ);
}
//-----------------------------------------------------------------------------
// BOOL SFileCloseArchive(HANDLE hMPQ);
//
// TODO: Test for archives > 4GB
BOOL WINAPI SFileCloseArchive(HANDLE hMPQ)
{
TMPQArchive * ha = (TMPQArchive *)hMPQ;
if(!IsValidMpqHandle(ha))
{
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
if(ha->dwFlags & MPQ_FLAG_CHANGED)
{
SListFileSaveToMpq(ha);
SaveMPQTables(ha);
}
FreeMPQArchive(ha);
return TRUE;
}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -