-
Notifications
You must be signed in to change notification settings - Fork 9
Key Files
Robert Jordan edited this page Mar 9, 2020
·
9 revisions
The CatSystem2 engine looks for 3 different key files in the install folder on launch.
| FileName | Usage |
|---|---|
cs2_debug_key.dat |
Enables debug mode features |
direct.dat |
Required to launch the game without a CD Used when /document/APP/<direct>1</direct>
|
key.dat |
Usage is not yet known Likely used for CD installations |
enum KeyFileType {
[Description("direct.dat")]
Direct = 0, // "direct.dat"
Key = 1, // "key.dat"
Cs2DebugKey = 2, // "cs2_debug_key.dat"
}
class KeyFileGenerator {
#region Constants
// Filenames of different key types, hardcoded in CatSystem2
const string DirectFileName = "direct.dat";
const string KeyFileName = "direct.dat";
const string Cs2DebugKeyFileName = "direct.dat";
// Encountered with cs2_open.exe Toolset v4.01, not fully understood
// Likely used in the absence of /document/APP/v_code in startup.xml
const string DefaultVCode1 = "open_cs2";
//const string Cs2VCode1 = "cs2 V_CODE";
#endregion
#region GenerateTocSeed
// Same as used in KIFINT archives
// CRC-32/BZIP2 (but we negate after every byte, instead of at the end)
public static uint GenerateTocSeed(string vcode) {
unchecked {
uint crc = 0xffffffff;
for (int i = 0; i < vcode.Length; i++) {
crc ^= ((uint) vcode[i] << 24);
for (int j = 0; j < 8; j++) {
if ((crc & 0x80000000) != 0)
crc = (crc << 1) ^ 0x04c11db7; // Polynomial
else
crc <<= 1;
}
crc = ~crc;
}
return crc;
}
}
#endregion
// Because "key.dat" requires volume serial number, we can supply our own when not running on the target machine
public static byte[] CreateKeyFile(KeyFileType keyType, [Optional] string vcode1, [Optional] uint? volumeSerialNumber) {
// Encountered with cs2_open.exe Toolset v4.01, not fully understood
// Likely used in the absence of /document/APP/v_code in startup.xml
// V_CODE (1) executable resource is ignore... I think
if (vcode1 == null)
vcode1 = "open_cs2";
// Extra step for "cs2_debug_key.dat": + "@@--cs2-debug-key--@@";
if (keyType == KeyFileType.Cs2DebugKey)
vcode1 += "@@--cs2-debug-key--@@";
// Use a CRC-32-style checksum of vcode for Mersenne Twister seed
uint vcode1Seed = GenerateTocSeed(vcode1String);
// Extra step for "key.dat": + volumeSerialNumber
if (keyType == KeyFileType.Key) {
if (!volumeSerialNumber.HasValue) {
// Get current machine's volume serial number
// For volume where /Windows is present
volumeSerialNumber = GetVolumeSerialNumber();
}
unchecked {
vcode1Seed += volumeSerialNumber.Value;
}
}
// Generate PRNG values for the key file's data and encryption key.
// One of the few times I’ve seen Mersenne Twister used to generate multiple numbers per seed in CatSystem2
MersenneTwister mt = new MersenneTwister();
mt.SetSeed(vcode1Seed);
uint[] tmpUIntBuffer = uint[16];
byte[] keyFileData = new byte[64]; // First 16 MT values
byte[] keyFileBlowfishKey = new byte[64]; // Next 16 MT values
// Generate key file data (mti=0,15)
for (int i = 0; i < 16; i++) {
tmpUIntBuffer[i] = mt.GenRand();
}
Buffer.BlockCopy(tmpUInts, 0, keyFileData, 0, 64);
// Generate key to encrypt key file data (mti=16,31)
for (int i = 0; i < 16; i++) {
tmpUIntBuffer[i] = mt.GenRand();
}
Buffer.BlockCopy(tmpUInts, 0, keyFileBlowfishKey, 0, 64);
// Encrypt key file data
Blowfish bf = new Blowfish();
blowfish.SetKey(keyFileBlowfishKey);
blowfish.Encrypt(keyFileData, 0, 64);
return keyFileData;
}
// Get volume serial number on the target machine
// Default volume is where /Windows is present
uint GetVolumeSerialNumber([Optional] string rootPathName) {
if (rootPathName == null) {
// Get serial number of volume where /Windows is present
rootPathName = Environment.GetSpecialFolder(Environment.SpecialFolder.Windows);
}
bool result GetVolumeInformation(
Path.GetRoot(windowsRoot),
null, 0,
out uint volumeSerialNumber,
out _,
out _,
null, 0);
volumeSerialNumber = currentSerialNumber;
if (!result)
throw new Win32Exceprion();
return volumeSerialNumber;
}
#region Native Methods
[DllImport("Kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
private extern static bool GetVolumeInformation(
[In] string rootPathName, // only input
StringBuilder volumeNameBuffer,
int volumeNameSize,
out uint volumeSerialNumber,
out uint maximumComponentLength,
out uint fileSystemFlags, // flags that we don’t need
StringBuilder fileSystemNameBuffer,
int nFileSystemNameSize);
#endregion
}