#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void DecodeLine2(char* pS) {
const char *gLong_key = "\x6c\x1b\x99\x5f\xb9\xcd\x5f\x13\xcb\x04\x20\x0e\x5e\x1c\xa1\x0e";
const char *gOther_long_key = "\x67\xa8\xd6\x26\xb6\xdd\x45\x1b\x32\x7e\x22\x13\x15\xc2\x94\x37";
const int gEncryption_method = 2;
int len;
int seed;
int i;
unsigned char c;
char* key;
key = (char*)gLong_key;
while (len > 0 && (pS[len - 1] == '\r' || pS[len - 1] == '\n')) {
len--;
pS[len] = '\0';
}
seed = len % 16;
for (i = 0; i < len; i++) {
c = pS[i];
if (i >= 2) {
if (pS[i - 1] == '/' && pS[i - 2] == '/') {
key = (char*)gOther_long_key;
}
}
if (gEncryption_method == 1) {
if (c == '\t') {
c = 0x9f;
}
c -= 0x20;
c ^= key[seed];
c &= 0x7f;
c += 0x20;
seed += 7;
seed %= 16;
if (c == 0x9f) {
c = '\t';
}
} else {
if (c == '\t') {
c = 0x80;
}
c -= 0x20;
if ((c & 0x80) == 0) {
c ^= key[seed] & 0x7f;
}
c += 0x20;
seed += 7;
seed %= 16;
if (c == 0x80) {
c = '\t';
}
}
pS[i] = c;
}
}
int main (int argc, char **argv)
{
// Carmageddon data file decryption utility
#define DECODE_CHAR_WITH_KEY(c,key) do { \
if (c == '\t') \
c = 128; \
c -= 32; \
if (c < 128) \
c ^= (key)[seed] & 0x7f; \
c += 32; \
if (c == 128) \
c = '\t'; \
seed = (seed + 7) % 16; \
} while (0)
const char *key1 = "\x6c\x1b\x99\x5f\xb9\xcd\x5f\x13\xcb\x04\x20\x0e\x5e\x1c\xa1\x0e";
const char *key2 = "\x67\xa8\xd6\x26\xb6\xdd\x45\x1b\x32\x7e\x22\x13\x15\xc2\x94\x37";
const char *key_to_use;
char line_buffer[1024];
char other_buffer[1024];
char *file_pathname = NULL;
int should_fix_b0rken_crypto = 1;
unsigned char c;
int weird_count;
int seed_backup;
int seed;
int len;
int i;
int j;
FILE *fp;
// process command-line arguments
for (i = 1; i < argc; i++)
{
if (strcmp (argv
[i
], "--dont-fix-b0rken-carmageddon-crypto") == 0)
should_fix_b0rken_crypto = 0;
else if (file_pathname == NULL)
file_pathname = argv[i];
}
// consistency check
if (file_pathname == NULL)
{
fprintf (stderr
, "usage: %s [--dont-fix-b0rken-carmageddon-crypto] <FILE.TXT>\n", argv
[0]);
}
// open the Carmageddon data file to decrypt
fp
= fopen (file_pathname
, "rb");
if (fp == NULL)
{
fprintf (stderr
, "error: couldn't open '%s'\n", argv
[1]);
}
// read it line per line
while (fgets (line_buffer
, sizeof (line_buffer
), fp
) != NULL
)
{
len
= (int) strlen (line_buffer
); // measure line length
// is this line encrypted ?
if (line_buffer[0] == '@')
{
// chop off all trailing CR and LFs from the input data
while ((len > 0) && ((line_buffer[len - 1] == '\r') || (line_buffer[len - 1] == '\n')))
line_buffer[--len] = 0;
#if 1
// start with the first key and initialize the algorithm seed to the remainder of the length of data / 16
key_to_use = key1;
seed = (len - 1) % 16;
// for each encrypted character...
for (i = 1; i < len; i++)
{
c = line_buffer[i]; // quick access to character
// when loading game data, Carmageddon does not switch the XOR cypher when the comments start
if ((key_to_use == key1) && should_fix_b0rken_crypto)
{
if ((i >= 3) && (line_buffer[i - 2] == '/') && (line_buffer[i - 3] == '/')) // pattern: "//?"
{
seed_backup = seed;
/*
weird_count = 0;
for (j = i; j < len; j++)
{
c = line_buffer[j];
DECODE_CHAR_WITH_KEY (c, key2); // decode character with the alternate key, but don't advance the seed
if (c > 0x7f)
weird_count++;
else if (c == ' ')
break;
}
if ((j < len) || (weird_count > (len - i) / 10))
{
// fprintf (stderr, "===> Switching keys at char %d\n", i);
key_to_use = key2;
}
/*/
DECODE_CHAR_WITH_KEY (c, key2); // decode character with the alternate key, but don't advance the seed
if ((i > 4) || (c == ' ') || (c == '\t') || (c == '/'))
{
// fprintf (stderr, "===> Switching keys at char %d\n", i);
key_to_use = key2; // if this character looks sensible enough, it's time to swap keys
}
c = line_buffer[i]; // reload character after decryption attempt with alternate key
seed = seed_backup; // and restore seed
}
}
DECODE_CHAR_WITH_KEY (c, key_to_use); // decode character and advance the seed
line_buffer[i - 1] = c; // write decoded character one position backwards to erase the '@' prefix
}
#else
i = len;
DecodeLine2 (&line_buffer[1]);
memmove (line_buffer
, &line_buffer
[1], len
);
#endif
line_buffer[i - 1] = 0; // terminate the decrypted string properly
}
// chop off all trailing CR and LFs from the decrypted data once again
while ((len > 0) && ((line_buffer[len - 1] == '\r') || (line_buffer[len - 1] == '\n')))
line_buffer[--len] = 0;
fprintf (stdout
, "%s\n", line_buffer
); // dump decrypted line to the standard output, with a proper newline at the end
}
fclose (fp
); // close the input file
exit (0); // and exit with a success code
}