Subversion Repositories Games.Carmageddon

Rev

Rev 1 | 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. void DecodeLine2(char* pS) {
  6.         const char *gLong_key       = "\x6c\x1b\x99\x5f\xb9\xcd\x5f\x13\xcb\x04\x20\x0e\x5e\x1c\xa1\x0e";
  7.         const char *gOther_long_key = "\x67\xa8\xd6\x26\xb6\xdd\x45\x1b\x32\x7e\x22\x13\x15\xc2\x94\x37";
  8.         const int gEncryption_method = 2;
  9.  
  10.     int len;
  11.     int seed;
  12.     int i;
  13.     unsigned char c;
  14.     char* key;
  15.  
  16.     len = strlen(pS);
  17.     key = (char*)gLong_key;
  18.     while (len > 0 && (pS[len - 1] == '\r' || pS[len - 1] == '\n')) {
  19.         len--;
  20.         pS[len] = '\0';
  21.     }
  22.     seed = len % 16;
  23.     for (i = 0; i < len; i++) {
  24.         c = pS[i];
  25.         if (i >= 2) {
  26.             if (pS[i - 1] == '/' && pS[i - 2] == '/') {
  27.                 key = (char*)gOther_long_key;
  28.             }
  29.         }
  30.         if (gEncryption_method == 1) {
  31.             if (c == '\t') {
  32.                 c = 0x9f;
  33.             }
  34.  
  35.             c -= 0x20;
  36.             c ^= key[seed];
  37.             c &= 0x7f;
  38.             c += 0x20;
  39.  
  40.             seed += 7;
  41.             seed %= 16;
  42.  
  43.             if (c == 0x9f) {
  44.                 c = '\t';
  45.             }
  46.         } else {
  47.             if (c == '\t') {
  48.                 c = 0x80;
  49.             }
  50.             c -= 0x20;
  51.             if ((c & 0x80) == 0) {
  52.                 c ^= key[seed] & 0x7f;
  53.             }
  54.             c += 0x20;
  55.  
  56.             seed += 7;
  57.             seed %= 16;
  58.  
  59.             if (c == 0x80) {
  60.                 c = '\t';
  61.             }
  62.         }
  63.         pS[i] = c;
  64.     }
  65. }
  66.  
  67. int main (int argc, char **argv)
  68. {
  69.         // Carmageddon data file decryption utility
  70.  
  71.         #define DECODE_CHAR_WITH_KEY(c,key) do { \
  72.                 if (c == '\t') \
  73.                         c = 128; \
  74.                 c -= 32; \
  75.                 if (c < 128) \
  76.                         c ^= (key)[seed] & 0x7f; \
  77.                 c += 32; \
  78.                 if (c == 128) \
  79.                         c = '\t'; \
  80.                 seed = (seed + 7) % 16; \
  81.         } while (0)
  82.  
  83.         const char *key1 = "\x6c\x1b\x99\x5f\xb9\xcd\x5f\x13\xcb\x04\x20\x0e\x5e\x1c\xa1\x0e";
  84.         const char *key2 = "\x67\xa8\xd6\x26\xb6\xdd\x45\x1b\x32\x7e\x22\x13\x15\xc2\x94\x37";
  85.         const char *key_to_use;
  86.         char line_buffer[1024];
  87.         char other_buffer[1024];
  88.         char *file_pathname = NULL;
  89.         int should_fix_b0rken_crypto = 1;
  90.         unsigned char c;
  91.         int weird_count;
  92.         int seed_backup;
  93.         int seed;
  94.         int len;
  95.         int i;
  96.         int j;
  97.         FILE *fp;
  98.  
  99.         // process command-line arguments
  100.         for (i = 1; i < argc; i++)
  101.         {
  102.                 if (strcmp (argv[i], "--dont-fix-b0rken-carmageddon-crypto") == 0)
  103.                         should_fix_b0rken_crypto = 0;
  104.                 else if (file_pathname == NULL)
  105.                         file_pathname = argv[i];
  106.         }
  107.  
  108.         // consistency check
  109.         if (file_pathname == NULL)
  110.         {
  111.                 fprintf (stderr, "usage: %s [--dont-fix-b0rken-carmageddon-crypto] <FILE.TXT>\n", argv[0]);
  112.                 exit (1);
  113.         }
  114.  
  115.         // open the Carmageddon data file to decrypt
  116.         fp = fopen (file_pathname, "rb");
  117.         if (fp == NULL)
  118.         {
  119.                 fprintf (stderr, "error: couldn't open '%s'\n", argv[1]);
  120.                 exit (1);
  121.         }
  122.  
  123.         // read it line per line
  124.         while (fgets (line_buffer, sizeof (line_buffer), fp) != NULL)
  125.         {
  126.                 len = (int) strlen (line_buffer); // measure line length
  127.  
  128.                 // is this line encrypted ?
  129.                 if (line_buffer[0] == '@')
  130.                 {
  131.                         // chop off all trailing CR and LFs from the input data
  132.                         while ((len > 0) && ((line_buffer[len - 1] == '\r') || (line_buffer[len - 1] == '\n')))
  133.                                 line_buffer[--len] = 0;
  134.  
  135. #if 1
  136.                         // start with the first key and initialize the algorithm seed to the remainder of the length of data / 16
  137.                         key_to_use = key1;
  138.                         seed = (len - 1) % 16;
  139.  
  140.                         // for each encrypted character...
  141.                         for (i = 1; i < len; i++)
  142.                         {
  143.                                 c = line_buffer[i]; // quick access to character
  144.  
  145.                                 // when loading game data, Carmageddon does not switch the XOR cypher when the comments start
  146.                                 if ((key_to_use == key1) && should_fix_b0rken_crypto)
  147.                                 {
  148.                                         if ((i >= 3) && (line_buffer[i - 2] == '/') && (line_buffer[i - 3] == '/')) // pattern: "//?"
  149.                                         {
  150.                                                 seed_backup = seed;
  151. /*
  152.                                                 weird_count = 0;
  153.                                                 for (j = i; j < len; j++)
  154.                                                 {
  155.                                                         c = line_buffer[j];
  156.                                                         DECODE_CHAR_WITH_KEY (c, key2); // decode character with the alternate key, but don't advance the seed
  157.                                                         if (c > 0x7f)
  158.                                                                 weird_count++;
  159.                                                         else if (c == ' ')
  160.                                                                 break;
  161.                                                 }
  162.                                                 if ((j < len) || (weird_count > (len - i) / 10))
  163.                                                 {
  164. //                                                      fprintf (stderr, "===> Switching keys at char %d\n", i);
  165.                                                         key_to_use = key2;
  166.                                                 }
  167. /*/
  168.                                                 DECODE_CHAR_WITH_KEY (c, key2); // decode character with the alternate key, but don't advance the seed
  169.                                                 if ((i > 4) || (c == ' ') || (c == '\t') || (c == '/'))
  170.                                                 {
  171. //                                                      fprintf (stderr, "===> Switching keys at char %d\n", i);
  172.                                                         key_to_use = key2; // if this character looks sensible enough, it's time to swap keys
  173.                                                 }
  174.  
  175.  
  176.                                                 c = line_buffer[i]; // reload character after decryption attempt with alternate key
  177.                                                 seed = seed_backup; // and restore seed
  178.                                         }
  179.                                 }
  180.  
  181.                                 DECODE_CHAR_WITH_KEY (c, key_to_use); // decode character and advance the seed
  182.                                 line_buffer[i - 1] = c; // write decoded character one position backwards to erase the '@' prefix
  183.                         }
  184. #else
  185. i = len;
  186. DecodeLine2 (&line_buffer[1]);
  187. memmove (line_buffer, &line_buffer[1], len);
  188. #endif
  189.                         line_buffer[i - 1] = 0; // terminate the decrypted string properly
  190.                 }
  191.  
  192.                 // chop off all trailing CR and LFs from the decrypted data once again
  193.                 while ((len > 0) && ((line_buffer[len - 1] == '\r') || (line_buffer[len - 1] == '\n')))
  194.                    line_buffer[--len] = 0;
  195.  
  196.                 fprintf (stdout, "%s\n", line_buffer); // dump decrypted line to the standard output, with a proper newline at the end
  197.         }
  198.  
  199.         fclose (fp); // close the input file
  200.         exit (0); // and exit with a success code
  201. }
  202.