📄 scommon.cpp
字号:
/*****************************************************************************/
/* SCommon.cpp Copyright (c) Ladislav Zezula 2003 */
/*---------------------------------------------------------------------------*/
/* Common functions for StormLib, used by all SFile*** modules */
/*---------------------------------------------------------------------------*/
/* Date Ver Who Comment */
/* -------- ---- --- ------- */
/* 24.03.03 1.00 Lad The first version of SFileCommon.cpp */
/* 19.11.03 1.01 Dan Big endian handling */
/* 12.06.04 1.01 Lad Renamed to SCommon.cpp */
/*****************************************************************************/
#define __STORMLIB_SELF__
#include "StormLib.h"
#include "SCommon.h"
char StormLibCopyright[] = "StormLib v 4.50 Copyright Ladislav Zezula 1998-2003";
//-----------------------------------------------------------------------------
// The buffer for decryption engine.
TMPQArchive * pFirstOpen = NULL; // The first member of MPQ archives chain
LCID lcLocale = LANG_NEUTRAL; // File locale
USHORT wPlatform = 0; // File platform
//-----------------------------------------------------------------------------
// Compression types
//
// Data compressions
//
// Can be combination of MPQ_COMPRESSION_PKWARE, MPQ_COMPRESSION_BZIP2
// and MPQ_COMPRESSION_ZLIB. Some newer compressions are not supported
// by older games. The table of supported compressions is here:
//
// MPQ_COMPRESSION_PKWARE - All games since Diablo I
// MPQ_COMPRESSION_ZLIB - Games since Starcraft
// MPQ_COMPRESSION_BZIP2 - Games since World of Warcraft
//
static int nDataCmp = MPQ_COMPRESSION_ZLIB;
//
// WAVE compressions by quality level
//
static int uWaveCmpLevel[] = {-1, 4, 2};
static int uWaveCmpType[] = {MPQ_COMPRESSION_PKWARE, 0x81, 0x81};
//-----------------------------------------------------------------------------
// Storm buffer functions
// Buffer for the decryption engine
#define STORM_BUFFER_SIZE 0x500
static DWORD StormBuffer[STORM_BUFFER_SIZE];
static BOOL bStormBufferCreated = FALSE;
int PrepareStormBuffer()
{
DWORD dwSeed = 0x00100001;
DWORD index1 = 0;
DWORD index2 = 0;
int i;
// Initialize the decryption buffer.
// Do nothing if already done.
if(bStormBufferCreated == FALSE)
{
for(index1 = 0; index1 < 0x100; index1++)
{
for(index2 = index1, i = 0; i < 5; i++, index2 += 0x100)
{
DWORD temp1, temp2;
dwSeed = (dwSeed * 125 + 3) % 0x2AAAAB;
temp1 = (dwSeed & 0xFFFF) << 0x10;
dwSeed = (dwSeed * 125 + 3) % 0x2AAAAB;
temp2 = (dwSeed & 0xFFFF);
StormBuffer[index2] = (temp1 | temp2);
}
}
bStormBufferCreated = TRUE;
}
return ERROR_SUCCESS;
}
//-----------------------------------------------------------------------------
// Encrypting and decrypting hash table
void EncryptHashTable(DWORD * pdwTable, BYTE * pbKey, DWORD dwLength)
{
DWORD dwSeed1 = 0x7FED7FED;
DWORD dwSeed2 = 0xEEEEEEEE;
DWORD ch; // One key character
// Prepare seeds
while(*pbKey != 0)
{
ch = toupper(*pbKey++);
dwSeed1 = StormBuffer[0x300 + ch] ^ (dwSeed1 + dwSeed2);
dwSeed2 = ch + dwSeed1 + dwSeed2 + (dwSeed2 << 5) + 3;
}
// Encrypt it
dwSeed2 = 0xEEEEEEEE;
while(dwLength-- > 0)
{
dwSeed2 += StormBuffer[0x400 + (dwSeed1 & 0xFF)];
ch = *pdwTable;
*pdwTable++ = ch ^ (dwSeed1 + dwSeed2);
dwSeed1 = ((~dwSeed1 << 0x15) + 0x11111111) | (dwSeed1 >> 0x0B);
dwSeed2 = ch + dwSeed2 + (dwSeed2 << 5) + 3;
}
}
void DecryptHashTable(DWORD * pdwTable, BYTE * pbKey, DWORD dwLength)
{
DWORD dwSeed1 = 0x7FED7FED;
DWORD dwSeed2 = 0xEEEEEEEE;
DWORD ch; // One key character
// Prepare seeds
while(*pbKey != 0)
{
ch = toupper(*pbKey++);
dwSeed1 = StormBuffer[0x300 + ch] ^ (dwSeed1 + dwSeed2);
dwSeed2 = ch + dwSeed1 + dwSeed2 + (dwSeed2 << 5) + 3;
}
// Decrypt it
dwSeed2 = 0xEEEEEEEE;
while(dwLength-- > 0)
{
dwSeed2 += StormBuffer[0x400 + (dwSeed1 & 0xFF)];
ch = *pdwTable ^ (dwSeed1 + dwSeed2);
dwSeed1 = ((~dwSeed1 << 0x15) + 0x11111111) | (dwSeed1 >> 0x0B);
dwSeed2 = ch + dwSeed2 + (dwSeed2 << 5) + 3;
*pdwTable++ = ch;
}
}
//-----------------------------------------------------------------------------
// Encrypting and decrypting block table
void EncryptBlockTable(DWORD * pdwTable, BYTE * pbKey, DWORD dwLength)
{
DWORD dwSeed1 = 0x7FED7FED;
DWORD dwSeed2 = 0xEEEEEEEE;
DWORD ch; // One key character
// Prepare seeds
while(*pbKey != 0)
{
ch = toupper(*pbKey++);
dwSeed1 = StormBuffer[0x300 + ch] ^ (dwSeed1 + dwSeed2);
dwSeed2 = ch + dwSeed1 + dwSeed2 + (dwSeed2 << 5) + 3;
}
// Decrypt it
dwSeed2 = 0xEEEEEEEE;
while(dwLength-- > 0)
{
dwSeed2 += StormBuffer[0x400 + (dwSeed1 & 0xFF)];
ch = *pdwTable;
*pdwTable++ = ch ^ (dwSeed1 + dwSeed2);
dwSeed1 = ((~dwSeed1 << 0x15) + 0x11111111) | (dwSeed1 >> 0x0B);
dwSeed2 = ch + dwSeed2 + (dwSeed2 << 5) + 3;
}
}
void DecryptBlockTable(DWORD * pdwTable, BYTE * pbKey, DWORD dwLength)
{
DWORD dwSeed1 = 0x7FED7FED;
DWORD dwSeed2 = 0xEEEEEEEE;
DWORD ch; // One key character
// Prepare seeds
while(*pbKey != 0)
{
ch = toupper(*pbKey++);
dwSeed1 = StormBuffer[0x300 + ch] ^ (dwSeed1 + dwSeed2);
dwSeed2 = ch + dwSeed1 + dwSeed2 + (dwSeed2 << 5) + 3;
}
// Encrypt it
dwSeed2 = 0xEEEEEEEE;
while(dwLength-- > 0)
{
dwSeed2 += StormBuffer[0x400 + (dwSeed1 & 0xFF)];
ch = *pdwTable ^ (dwSeed1 + dwSeed2);
dwSeed1 = ((~dwSeed1 << 0x15) + 0x11111111) | (dwSeed1 >> 0x0B);
dwSeed2 = ch + dwSeed2 + (dwSeed2 << 5) + 3;
*pdwTable++ = ch;
}
}
//-----------------------------------------------------------------------------
// Functions tries to get file decryption key. The trick comes from block
// positions which are stored at the begin of each compressed file. We know the
// file size, that means we know number of blocks that means we know the first
// DWORD value in block position. And if we know encrypted and decrypted value,
// we can find the decryption key !!!
//
// hf - MPQ file handle
// block - DWORD array of block positions
// ch - Decrypted value of the first block pos
DWORD DetectFileSeed(DWORD * block, DWORD decrypted)
{
DWORD saveSeed1;
DWORD temp = *block ^ decrypted; // temp = seed1 + seed2
temp -= 0xEEEEEEEE; // temp = seed1 + StormBuffer[0x400 + (seed1 & 0xFF)]
for(int i = 0; i < 0x100; i++) // Try all 255 possibilities
{
DWORD seed1;
DWORD seed2 = 0xEEEEEEEE;
DWORD ch;
// Try the first DWORD (We exactly know the value)
seed1 = temp - StormBuffer[0x400 + i];
seed2 += StormBuffer[0x400 + (seed1 & 0xFF)];
ch = block[0] ^ (seed1 + seed2);
if(ch != decrypted)
continue;
// Add 1 because we are decrypting block positions
saveSeed1 = seed1 + 1;
// If OK, continue and test the second value. We don't know exactly the value,
// but we know that the second one has lower 16 bits set to zero
// (no compressed block is larger than 0xFFFF bytes)
seed1 = ((~seed1 << 0x15) + 0x11111111) | (seed1 >> 0x0B);
seed2 = ch + seed2 + (seed2 << 5) + 3;
seed2 += StormBuffer[0x400 + (seed1 & 0xFF)];
ch = block[1] ^ (seed1 + seed2);
if((ch & 0xFFFF0000) == 0)
return saveSeed1;
}
return 0;
}
// Function tries to detect file seed. It expectes at least two uncompressed bytes
DWORD DetectFileSeed2(DWORD * pdwBlock, UINT nDwords, ...)
{
va_list argList;
DWORD dwDecrypted[0x10];
DWORD saveSeed1;
DWORD dwTemp;
DWORD i, j;
// We need at least two DWORDS to detect the seed
if(nDwords < 0x02 || nDwords > 0x10)
return 0;
va_start(argList, nDwords);
for(i = 0; i < nDwords; i++)
dwDecrypted[i] = va_arg(argList, DWORD);
va_end(argList);
dwTemp = (*pdwBlock ^ dwDecrypted[0]) - 0xEEEEEEEE;
for(i = 0; i < 0x100; i++) // Try all 255 possibilities
{
DWORD seed1;
DWORD seed2 = 0xEEEEEEEE;
DWORD ch;
// Try the first DWORD
seed1 = dwTemp - StormBuffer[0x400 + i];
seed2 += StormBuffer[0x400 + (seed1 & 0xFF)];
ch = pdwBlock[0] ^ (seed1 + seed2);
if(ch != dwDecrypted[0])
continue;
saveSeed1 = seed1;
// If OK, continue and test all bytes.
for(j = 1; j < nDwords; j++)
{
seed1 = ((~seed1 << 0x15) + 0x11111111) | (seed1 >> 0x0B);
seed2 = ch + seed2 + (seed2 << 5) + 3;
seed2 += StormBuffer[0x400 + (seed1 & 0xFF)];
ch = pdwBlock[j] ^ (seed1 + seed2);
if(ch == dwDecrypted[j] && j == nDwords - 1)
return saveSeed1;
}
}
return 0;
}
//-----------------------------------------------------------------------------
// Encrypting and decrypting MPQ blocks
void EncryptMPQBlock(DWORD * block, DWORD dwLength, DWORD dwSeed1)
{
DWORD dwSeed2 = 0xEEEEEEEE;
DWORD ch;
// Round to DWORDs
dwLength >>= 2;
while(dwLength-- > 0)
{
dwSeed2 += StormBuffer[0x400 + (dwSeed1 & 0xFF)];
ch = *block;
*block++ = ch ^ (dwSeed1 + dwSeed2);
dwSeed1 = ((~dwSeed1 << 0x15) + 0x11111111) | (dwSeed1 >> 0x0B);
dwSeed2 = ch + dwSeed2 + (dwSeed2 << 5) + 3;
}
}
void DecryptMPQBlock(DWORD * block, DWORD dwLength, DWORD dwSeed1)
{
DWORD dwSeed2 = 0xEEEEEEEE;
DWORD ch;
// Round to DWORDs
dwLength >>= 2;
while(dwLength-- > 0)
{
dwSeed2 += StormBuffer[0x400 + (dwSeed1 & 0xFF)];
ch = *block ^ (dwSeed1 + dwSeed2);
dwSeed1 = ((~dwSeed1 << 0x15) + 0x11111111) | (dwSeed1 >> 0x0B);
dwSeed2 = ch + dwSeed2 + (dwSeed2 << 5) + 3;
*block++ = ch;
}
}
DWORD DecryptHashIndex(TMPQArchive * ha, const char * szFileName)
{
BYTE * pbKey = (BYTE *)szFileName;
DWORD dwSeed1 = 0x7FED7FED;
DWORD dwSeed2 = 0xEEEEEEEE;
DWORD ch;
while(*pbKey != 0)
{
ch = toupper(*pbKey++);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -