Subversion Repositories Games.Carmageddon

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4.  
  5.  
  6. int main (int argc, char **argv)
  7. {
  8.         // Carmageddon data file decryption utility
  9.  
  10.         #define DECODE_CHAR_WITH_KEY(c,key,should_bump_seed) do { \
  11.                 if (c == '\t') \
  12.                         c = 0x80; \
  13.                 c -= 32; \
  14.                 if ((c & 0x80) == 0) \
  15.                         c ^= (key)[seed] & 0x7f; \
  16.                 c += 32; \
  17.                 if (c == 0x80) \
  18.                         c = '\t'; \
  19.                 if (should_bump_seed) \
  20.                         seed = (seed + 7) % 16; \
  21.         } while (0)
  22.  
  23.         const char *key1 = "\x6c\x1b\x99\x5f\xb9\xcd\x5f\x13\xcb\x04\x20\x0e\x5e\x1c\xa1\x0e";
  24.         const char *key2 = "\x67\xa8\xd6\x26\xb6\xdd\x45\x1b\x32\x7e\x22\x13\x15\xc2\x94\x37";
  25.         const char *key_to_use;
  26.         char line_buffer[1024];
  27.         char *file_pathname = NULL;
  28.         int should_fix_b0rken_crypto = 1;
  29.         unsigned char c;
  30.         int seed;
  31.         int len;
  32.         int i;
  33.         FILE *fp;
  34.  
  35.         // process command-line arguments
  36.         for (i = 1; i < argc; i++)
  37.         {
  38.                 if (strcmp (argv[i], "--dont-fix-b0rken-carmageddon-crypto") == 0)
  39.                         should_fix_b0rken_crypto = 0;
  40.                 else if (file_pathname == NULL)
  41.                         file_pathname = argv[i];
  42.         }
  43.  
  44.         // consistency check
  45.         if (file_pathname == NULL)
  46.         {
  47.                 fprintf (stderr, "usage: %s [--dont-fix-b0rken-carmageddon-crypto] <FILE.TXT>\n", argv[0]);
  48.                 exit (1);
  49.         }
  50.  
  51.         // open the Carmageddon data file to decrypt
  52.         fp = fopen (file_pathname, "rb");
  53.         if (fp == NULL)
  54.         {
  55.                 fprintf (stderr, "error: couldn't open '%s'\n", argv[1]);
  56.                 exit (1);
  57.         }
  58.  
  59.         // read it line per line
  60.         while (fgets (line_buffer, sizeof (line_buffer), fp) != NULL)
  61.         {
  62.                 len = (int) strlen (line_buffer); // measure line length
  63.  
  64.                 // is this line encrypted ?
  65.                 if (line_buffer[0] == '@')
  66.                 {
  67.                         // chop off all trailing CR and LFs from the input data
  68.                         while ((len > 0) && ((line_buffer[len - 1] == '\r') || (line_buffer[len - 1] == '\n')))
  69.                                 line_buffer[--len] = 0;
  70.  
  71.                         // start with the first key and initialize the algorithm seed to the remainder of the length of data / 16
  72.                         key_to_use = key1;
  73.                         seed = (len - 1) % 16;
  74.  
  75.                         // for each encrypted character...
  76.                         for (i = 1; i < len; i++)
  77.                         {
  78.                                 c = line_buffer[i]; // quick access to character
  79.  
  80.                                 // when loading game data, Carmageddon does not switch the XOR cypher when the comments start
  81.                                 if (should_fix_b0rken_crypto && (i >= 3) && (line_buffer[i - 2] == '/') && (line_buffer[i - 3] == '/'))
  82.                                 {
  83.                                         DECODE_CHAR_WITH_KEY (c, key2, 0); // decode character with the alternate key, but don't advance the seed
  84.                                         if ((i > 4) || (c == ' ') || (c == '\t') || (c == '/'))
  85.                                                 key_to_use = key2; // if this character looks sensible enough, it's time to swap keys
  86.                                         c = line_buffer[i]; // reload character after decryption attempt with alternate key
  87.                                 }
  88.  
  89.                                 DECODE_CHAR_WITH_KEY (c, key_to_use, 1); // decode character and advance the seed
  90.                                 line_buffer[i - 1] = c; // write decoded character one position backwards to erase the '@' prefix
  91.                         }
  92.  
  93.                         line_buffer[i - 1] = 0; // terminate the decrypted string properly
  94.                 }
  95.  
  96.                 // chop off all trailing CR and LFs from the decrypted data once again
  97.                 while ((len > 0) && ((line_buffer[len - 1] == '\r') || (line_buffer[len - 1] == '\n')))
  98.                    line_buffer[--len] = 0;
  99.  
  100.                 fprintf (stdout, "%s\n", line_buffer); // dump decrypted line to the standard output, with a proper newline at the end
  101.         }
  102.  
  103.         fclose (fp); // close the input file
  104.         exit (0); // and exit with a success code
  105. }
  106.