#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (int argc, char **argv)
{
// Carmageddon data file decryption utility
#define DECODE_CHAR_WITH_KEY(c,key,should_bump_seed) do { \
if (c == '\t') \
c = 0x80; \
c -= 32; \
if ((c & 0x80) == 0) \
c ^= (key)[seed] & 0x7f; \
c += 32; \
if (c == 0x80) \
c = '\t'; \
if (should_bump_seed) \
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 *file_pathname = NULL;
int should_fix_b0rken_crypto = 1;
unsigned char c;
int seed;
int len;
int i;
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;
// 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 (should_fix_b0rken_crypto && (i >= 3) && (line_buffer[i - 2] == '/') && (line_buffer[i - 3] == '/'))
{
DECODE_CHAR_WITH_KEY (c, key2, 0); // decode character with the alternate key, but don't advance the seed
if ((i > 4) || (c == ' ') || (c == '\t') || (c == '/'))
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
}
DECODE_CHAR_WITH_KEY (c, key_to_use, 1); // decode character and advance the seed
line_buffer[i - 1] = c; // write decoded character one position backwards to erase the '@' prefix
}
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
}