diff options
Diffstat (limited to 'contrib/very-important-source-code/important-stuff/game_data.c')
-rw-r--r-- | contrib/very-important-source-code/important-stuff/game_data.c | 166 |
1 files changed, 166 insertions, 0 deletions
diff --git a/contrib/very-important-source-code/important-stuff/game_data.c b/contrib/very-important-source-code/important-stuff/game_data.c new file mode 100644 index 00000000..48774187 --- /dev/null +++ b/contrib/very-important-source-code/important-stuff/game_data.c @@ -0,0 +1,166 @@ +#define _GNU_SOURCE +#include <stdio.h> +#include <stdlib.h> +#include <mxml.h> +#include <stdbool.h> +#include <stdint.h> +#include <sys/stat.h> +#include <string.h> + +//First 5 bytes is MD5 hash of "NaturalPoint" +static uint8_t secret_key[] = {0x0e, 0x9a, 0x63, 0x71, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; +static uint8_t S[256] = {0}; + +static char *decoded = NULL; + +static mxml_node_t *xml = NULL; +static mxml_node_t *tree = NULL; + +static void ksa(uint8_t key[], size_t len) +{ + unsigned int i, j; + for(i = 0; i < 256; ++i){ + S[i] = i; + } + j = 0; + for(i = 0; i < 256; ++i){ + j = (j + S[i] + key[i % len]) % 256; + uint8_t tmp = S[i]; + S[i] = S[j]; + S[j] = tmp; + } +} + +static uint8_t rc4() +{ + static uint8_t i = 0; + static uint8_t j = 0; + + i += 1; + j += S[i]; + uint8_t tmp = S[i]; + S[i] = S[j]; + S[j] = tmp; + return S[(S[i] + S[j]) % 256]; +} + +static bool decrypt_file(const char *fname, bool from_update) +{ + uint32_t header[5]; + size_t datlen; + ksa(secret_key, 16); + FILE *inp; + struct stat fst; + + if((inp = fopen(fname, "rb")) == NULL){ + printf("Can't open input file '%s'", fname); + return false; + } + + if(fstat(fileno(inp), &fst) != 0){ + fclose(inp); + printf("Cannot stat file '%s'\n", fname); + return false; + } + + if(from_update){ + if(fread(&header, sizeof(uint32_t), 5, inp) != 5){ + fclose(inp); + printf("Can't read the header - file '%s' is less than 20 bytes long?\n", fname); + return false; + } + datlen = header[4]; + }else{ + datlen = fst.st_size; + } + if((decoded = (char *)malloc(datlen+1)) == NULL){ + printf("malloc failed!\n"); + return false; + } + memset(decoded, 0, datlen+1); + size_t i; + size_t len = fread(decoded, 1, datlen, inp); + (void) len; + for(i = 0; i < datlen; ++i) decoded[i] ^= rc4(); + fclose(inp); + + //inp = fopen("tmp.dump", "w"); + //fwrite(decoded, 1, datlen, inp); + //fclose(inp); + + return true; +} + +static bool game_data_init(const char *fname, bool from_update) +{ + static bool initialized = false; + if(initialized){ + return true; + } + if(!decrypt_file(fname, from_update)){ + printf("Error decrypting file!\n"); + return false; + } + xml = mxmlNewXML("1.0"); + tree = mxmlLoadString(xml, decoded, MXML_TEXT_CALLBACK); + return (tree != NULL); +} + +static void game_data_close() +{ + mxmlDelete(tree); + free(decoded); +} + +#define ltr_int_log_message(...) fprintf(stderr, __VA_ARGS__) + +static void remove_newlines(const char* str, char* out, int out_len) +{ + int i, j; + int len = strlen(str); + for (i = 0, j = 0; str[i] && j + 1 < out_len; i++) + { + if (str[i] == '\r' || str[i] == '\n') + continue; + out[j++] = str[i]; + } + if (j < out_len) + out[j] = '\0'; +} + +bool get_game_data(const char *input_fname, const char *output_fname, bool from_update) +{ + FILE *outfile = NULL; + if((outfile = (output_fname ? fopen(output_fname, "w") : stdout)) == NULL){ + ltr_int_log_message("Can't open the output file '%s'!\n", output_fname); + return false; + } + if(!game_data_init(input_fname, from_update)){ + ltr_int_log_message("Can't process the data file '%s'!\n", input_fname); + return false; + } + + mxml_node_t *game; + const char *name; + const char *id; + for(game = mxmlFindElement(tree, tree, "Game", NULL, NULL, MXML_DESCEND); + game != NULL; + game = mxmlFindElement(game, tree, "Game", NULL, NULL, MXML_DESCEND)) + { + name = mxmlElementGetAttr(game, "Name"); + id = mxmlElementGetAttr(game, "Id"); + + mxml_node_t *appid = mxmlFindElement(game, game, "ApplicationID", NULL, NULL, MXML_DESCEND); + char name_[256]; + remove_newlines(name, name_, sizeof(name_)); + if(appid == NULL) + fprintf(outfile, "%s \"%s\"\n", id, name_); + else + fprintf(outfile, "%s \"%s\" (%s)\n", id, name_, appid->child->value.text.string); + } + fclose(outfile); + game_data_close(); + return true; +} + +int main(int argc, char** argv) { return argc > 1 && get_game_data(argv[1], NULL, false); } |