📄 scommon.cpp
字号:
dwSeed1 = StormBuffer[0x000 + ch] ^ (dwSeed1 + dwSeed2);
dwSeed2 = ch + dwSeed1 + dwSeed2 + (dwSeed2 << 5) + 3;
}
return (dwSeed1 & (ha->pHeader->dwHashTableSize - 1));
}
DWORD DecryptName1(const char * szFileName)
{
BYTE * pbKey = (BYTE *)szFileName;
DWORD dwSeed1 = 0x7FED7FED;
DWORD dwSeed2 = 0xEEEEEEEE;
DWORD ch;
while(*pbKey != 0)
{
ch = toupper(*pbKey++);
dwSeed1 = StormBuffer[0x100 + ch] ^ (dwSeed1 + dwSeed2);
dwSeed2 = ch + dwSeed1 + dwSeed2 + (dwSeed2 << 5) + 3;
}
return dwSeed1;
}
DWORD DecryptName2(const char * szFileName)
{
BYTE * pbKey = (BYTE *)szFileName;
DWORD dwSeed1 = 0x7FED7FED;
DWORD dwSeed2 = 0xEEEEEEEE;
int ch;
while(*pbKey != 0)
{
ch = toupper(*pbKey++);
dwSeed1 = StormBuffer[0x200 + ch] ^ (dwSeed1 + dwSeed2);
dwSeed2 = ch + dwSeed1 + dwSeed2 + (dwSeed2 << 5) + 3;
}
return dwSeed1;
}
DWORD DecryptFileSeed(const char * szFileName)
{
BYTE * pbKey = (BYTE *)szFileName;
DWORD dwSeed1 = 0x7FED7FED; // EBX
DWORD dwSeed2 = 0xEEEEEEEE; // ESI
DWORD ch;
while(*pbKey != 0)
{
ch = toupper(*pbKey++); // ECX
dwSeed1 = StormBuffer[0x300 + ch] ^ (dwSeed1 + dwSeed2);
dwSeed2 = ch + dwSeed1 + dwSeed2 + (dwSeed2 << 5) + 3;
}
return dwSeed1;
}
TMPQHash * GetHashEntry(TMPQArchive * ha, const char * szFileName)
{
TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize;
TMPQHash * pHash0; // File hash entry (start)
TMPQHash * pHash; // File hash entry (current)
DWORD dwIndex = (DWORD)(DWORD_PTR)szFileName;
DWORD dwName1;
DWORD dwName2;
// If filename is given by index, we have to search all hash entries for the right index.
if(dwIndex <= ha->pHeader->dwBlockTableSize)
{
// Pass all the hash entries and find the
for(pHash = ha->pHashTable; pHash < pHashEnd; pHash++)
{
if(pHash->dwBlockIndex == dwIndex)
return pHash;
}
return NULL;
}
// Decrypt name and block index
dwIndex = DecryptHashIndex(ha, szFileName);
dwName1 = DecryptName1(szFileName);
dwName2 = DecryptName2(szFileName);
pHash = pHash0 = ha->pHashTable + dwIndex;
// Look for hash index
while(pHash->dwBlockIndex != HASH_ENTRY_FREE)
{
if(pHash->dwName1 == dwName1 && pHash->dwName2 == dwName2 && pHash->dwBlockIndex != HASH_ENTRY_DELETED)
return pHash;
// Move to the next hash entry
if(++pHash >= pHashEnd)
pHash = ha->pHashTable;
if(pHash == pHash0)
break;
}
// File was not found
return NULL;
}
// Retrieves the locale-specific hash entry
TMPQHash * GetHashEntryEx(TMPQArchive * ha, const char * szFileName, LCID lcLocale)
{
TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize;
TMPQHash * pHash0 = NULL; // Language-neutral hash entry
TMPQHash * pHashX = NULL; // Language-speficic
TMPQHash * pHash = GetHashEntry(ha, szFileName);
if(pHash != NULL)
{
TMPQHash * pHashStart = pHash;
DWORD dwName1 = pHash->dwName1;
DWORD dwName2 = pHash->dwName2;
while(pHash->dwBlockIndex != HASH_ENTRY_FREE)
{
if(pHash->dwName1 == dwName1 && pHash->dwName2 == dwName2)
{
if(pHash->lcLocale == LANG_NEUTRAL)
pHash0 = pHash;
if(pHash->lcLocale == lcLocale)
pHashX = pHash;
// If both found, break the loop
if(pHash0 != NULL && pHashX != NULL)
break;
}
if(++pHash >= pHashEnd)
pHash = ha->pHashTable;
if(pHash == pHashStart)
return NULL;
}
if(lcLocale != LANG_NEUTRAL && pHashX != NULL)
return pHashX;
if(pHash0 != NULL)
return pHash0;
return NULL;
}
return pHash;
}
// Encrypts file name and gets the hash entry
// Returns the hash pointer, which is always within the allocated array
TMPQHash * FindFreeHashEntry(TMPQArchive * ha, const char * szFileName)
{
TMPQHash * pHashEnd = ha->pHashTable + ha->pHeader->dwHashTableSize;
TMPQHash * pHash0; // File hash entry (search start)
TMPQHash * pHash; // File hash entry
DWORD dwIndex = DecryptHashIndex(ha, szFileName);
DWORD dwName1 = DecryptName1(szFileName);
DWORD dwName2 = DecryptName2(szFileName);
DWORD dwBlockIndex = 0xFFFFFFFF;
// Save the starting hash position
pHash = pHash0 = ha->pHashTable + dwIndex;
// Look for the first free hash entry. Can be also a deleted entry
while(pHash->dwBlockIndex < HASH_ENTRY_DELETED)
{
if(++pHash >= pHashEnd)
pHash = ha->pHashTable;
if(pHash == pHash0)
return NULL;
}
// Fill the hash entry with the informations about the file name
pHash->dwName1 = dwName1;
pHash->dwName2 = dwName2;
pHash->lcLocale = (USHORT)lcLocale;
pHash->wPlatform = wPlatform;
// Now we have to find a free block entry
for(dwIndex = 0; dwIndex < ha->pHeader->dwBlockTableSize; dwIndex++)
{
TMPQBlock * pBlock = ha->pBlockTable + dwIndex;
if((pBlock->dwFlags & MPQ_FILE_EXISTS) == 0)
{
dwBlockIndex = dwIndex;
break;
}
}
// If no free block entry found, we have to use the index
// at the end of the current block table
if(dwBlockIndex == 0xFFFFFFFF)
dwBlockIndex = ha->pHeader->dwBlockTableSize;
pHash->dwBlockIndex = dwBlockIndex;
return pHash;
}
//-----------------------------------------------------------------------------
// Checking for valid archive handle and valid file handle
BOOL IsValidMpqHandle(TMPQArchive * ha)
{
if(ha == NULL || IsBadReadPtr(ha, sizeof(TMPQArchive)))
return FALSE;
if(ha->pHeader == NULL || IsBadReadPtr(ha->pHeader, sizeof(TMPQHeader)))
return FALSE;
return (ha->pHeader->dwID == ID_MPQ);
}
BOOL IsValidFileHandle(TMPQFile * hf)
{
if(hf == NULL || IsBadReadPtr(hf, sizeof(TMPQFile)))
return FALSE;
if(hf->hFile != INVALID_HANDLE_VALUE)
return TRUE;
return IsValidMpqHandle(hf->ha);
}
// This function writes a local file into the MPQ archive.
// Returns 0 if OK, otherwise error code.
// TODO: Test for archives > 4GB
int AddFileToArchive(
TMPQArchive * ha,
HANDLE hFile,
const char * szArchivedName,
DWORD dwFlags,
DWORD dwQuality,
int nFileType,
BOOL * pbReplaced)
{
LARGE_INTEGER RelativePos = {0};
LARGE_INTEGER FilePos = {0};
LARGE_INTEGER TempPos;
TMPQBlockEx * pBlockEx = NULL; // Entry in the extended block table
TMPQBlock * pBlock = NULL; // Entry in the block table
TMPQHash * pHash = NULL; // Entry in the hash table
DWORD * pdwBlockPos = NULL; // Block position table (compressed files only)
BYTE * pbFileData = NULL; // Uncompressed (source) data
BYTE * pbCompressed = NULL; // Compressed (target) data
BYTE * pbToWrite = NULL; // Data to write to the file
DWORD dwBlockPosLen = 0; // Length of the block table positions
DWORD dwTransferred = 0; // Number of bytes written into archive file
DWORD dwFileSize = 0; // Size of the file to add
DWORD dwSeed1 = 0; // Encryption seed
DWORD nBlocks = 0; // Number of file blocks
DWORD nBlock = 0; // Index of the currently written block
BOOL bReplaced = FALSE; // TRUE if replaced, FALSE if added
int nCmpFirst = nDataCmp; // Compression for the first data block
int nCmpNext = nDataCmp; // Compression for the next data blocks
int nCmp = nDataCmp; // Current compression
int nCmpLevel = -1; // Compression level
int nError = ERROR_SUCCESS;
// Set the correct compression types
if(dwFlags & MPQ_FILE_COMPRESS_PKWARE)
nCmpFirst = nCmpNext = MPQ_COMPRESSION_PKWARE;
if(dwFlags & MPQ_FILE_COMPRESS_MULTI)
{
if(nFileType == SFILE_TYPE_DATA)
nCmpFirst = nCmpNext = nDataCmp;
if(nFileType == SFILE_TYPE_WAVE)
{
nCmpNext = uWaveCmpType[dwQuality];
nCmpLevel = uWaveCmpLevel[dwQuality];
}
}
// Check if the file already exists in the archive
if(nError == ERROR_SUCCESS)
{
if((pHash = GetHashEntryEx(ha, szArchivedName, lcLocale)) != NULL)
{
if(pHash->lcLocale == lcLocale)
{
if((dwFlags & MPQ_FILE_REPLACEEXISTING) == 0)
{
nError = ERROR_ALREADY_EXISTS;
pHash = NULL;
}
else
bReplaced = TRUE;
}
else
pHash = NULL;
}
if(nError == ERROR_SUCCESS && pHash == NULL)
{
pHash = FindFreeHashEntry(ha, szArchivedName);
if(pHash == NULL)
nError = ERROR_HANDLE_DISK_FULL;
}
}
// Get the block table entry for the file
if(nError == ERROR_SUCCESS)
{
DWORD dwFileSizeHigh = 0;
// Get the size of the added file
dwFileSize = GetFileSize(hFile, &dwFileSizeHigh);
if(dwFileSizeHigh != 0)
nError = ERROR_PARAMETER_QUOTA_EXCEEDED;
// Fix the flags, if the file is too small
if(dwFileSize < 0x04)
dwFlags &= ~(MPQ_FILE_ENCRYPTED | MPQ_FILE_FIXSEED);
if(dwFileSize < 0x20)
dwFlags &= ~MPQ_FILE_COMPRESSED;
if(pHash->dwBlockIndex == HASH_ENTRY_FREE)
pHash->dwBlockIndex = ha->pHeader->dwBlockTableSize;
// The block table index cannot be larger than hash table size
if(pHash->dwBlockIndex >= ha->pHeader->dwHashTableSize)
nError = ERROR_HANDLE_DISK_FULL;
}
// The file will be stored after the end of the last archived file
// (i.e. at old position of archived file
if(nError == ERROR_SUCCESS)
{
TMPQBlock * pBlockEnd = ha->pBlockTable + ha->pHeader->dwBlockTableSize;
const char * szTemp = strrchr(szArchivedName, '\\');
// Get the position of the first file
RelativePos.QuadPart = ha->pHeader->dwHeaderSize;
// Find the position of the last file. It has to be after the last archived file
// (Do not use the dwArchiveSize here, because it may or may not
// include the hash table at the end of the file
pBlockEx = ha->pExtBlockTable;
for(pBlock = ha->pBlockTable; pBlock < pBlockEnd; pBlock++, pBlockEx++)
{
if(pBlock->dwFlags & MPQ_FILE_EXISTS)
{
TempPos.HighPart = pBlockEx->wFilePosHigh;
TempPos.LowPart = pBlock->dwFilePos;
TempPos.QuadPart += pBlock->dwCSize;
if(TempPos.QuadPart > RelativePos.QuadPart)
RelativePos = TempPos;
}
}
// When format V1, we cannot exceed 4 GB
if(ha->pHeader->wFormatVersion == MPQ_FORMAT_VERSION_1)
{
TempPos.QuadPart = ha->MpqPos.QuadPart + RelativePos.QuadPart;
TempPos.QuadPart += ha->pHeader->dwHashTableSize * sizeof(TMPQHash);
TempPos.QuadPart += ha->pHeader->dwBlockTableSize * sizeof(TMPQBlock);
TempPos.QuadPart += dwFileSize;
if(TempPos.HighPart != 0)
nError = ERROR_DISK_FULL;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -