/* * Copyright 2007 Michael Buckley * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the Free * Software Foundation; either version 2 of the license or (at your option) * any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the Gnu Public License for more * details. * * You should have received a copy of the GNU General Public License along * with this program; if not, If not, see . */ #include #include #if defined (__mac_os) || defined (__APPLE__) #define CASE_INSENSITIVE_FILESYSTEM 1 #else #define CASE_INSENSITIVE_FILESYSTEM 0 #endif #undef FALSE #undef TRUE typedef enum { FALSE, TRUE } bool; #define COOKIE "MegaHALv8" #define BYTE1 unsigned char #define BYTE2 unsigned short #define BYTE4 unsigned long static void convert_brain(FILE* in, FILE* out); static bool convert_tree(FILE* in, FILE* out, int end_position); static BYTE2 swap2(BYTE2 val); static BYTE4 swap4(BYTE4 val); int main(int argc, char* argv[]) { if(argc != 3){ fprintf(stdout, "Usage: %s infile outfile\n\n", argv[0]); } /* Check if the two files are the same. This is a stupid test, because it * bases the check on the path, and two different paths can point to the * same file, but this should hopefully prevent people from killing their * megahal brain. */ bool same_file; #if CASE_INSENSITIVE_FILESYSTEM == 1 same_file = strcasecmp(argv[1], argv[2]) == 0; #else same_file = strcmp(argv[1], argv[2]) == 0; #endif if(same_file == TRUE) { fprintf(stderr, "Error: The two files are the same\n"); fprintf(stderr, "Please choose two different filenames\n\n"); return 0; } FILE* in = fopen(argv[1], "r"); if(in == NULL){ fprintf(stderr, "Could not open the file %s for reading", argv[1]); return 0; } FILE* out = fopen(argv[2], "w"); if(in == NULL){ fprintf(stderr, "Could not open the file %s for writing", argv[2]); return 0; } convert_brain(in, out); } void convert_brain(FILE* in, FILE* out) { /* Copy over the file signature and model order to make sure it's a valid * MegaHAL file. */ char cookie[10]; memset(cookie, 0, 10); if(fread(cookie, sizeof(char), strlen(COOKIE), in) < strlen(COOKIE)){ fprintf(stderr, "Error: Reached file end before file signature.\n"); } if(strcmp(cookie, COOKIE)!=0){ fprintf(stderr, "This is not a Megahal Brain.\n"); return; } fwrite(cookie, 1, strlen(COOKIE), out); BYTE1 order = fgetc(in); if(feof(in) != 0){ fprintf(stderr, "Error: reached file end before model order.\n"); return; } fputc(order, out); /* Go to the end of the file and work backwards until the beginning of the * dictionary, and note the position. */ fseek(in, -1, SEEK_END); int file_size = ftell(in) + 1; if(ftell(in) < 0){ fprintf(stderr, "Error: Rewound past start of file\n\n"); } char c; while(TRUE){ c = fgetc(in); if(c == '<'){ fseek(in, -2, SEEK_CUR); if(ftell(in) < 0){ fprintf(stderr, "Error: Rewound past start of file\n\n"); } c = fgetc(in); if(c == 7){ int position = ftell(in); char test[8]; memset(test, 0, 8); fread(test, sizeof(char), 7, in); if(strcmp(test, "") == 0){ fseek(in, position - ftell(in) -5, SEEK_CUR); break; } } } fseek(in, -2, SEEK_CUR); if(ftell(in) < 0){ fprintf(stderr, "Error: Rewound past start of file\n\n"); } } int end_position = ftell(in); /* Start after the model order and swap the endianess of the tree nodes * until the start of the dictionary. */ fseek(in, 10, SEEK_SET); if(convert_tree(in, out, end_position) == FALSE){ return; } /* Swap the endianess of the dictionary size. */ BYTE4 size; if(fread(&size, sizeof(BYTE4), 1, in) < 1){ fprintf(stderr, "Error: reached file end before dictionary size.\n\n"); return; } size = swap4(size); fwrite(&size, sizeof(BYTE4), 1, out); /* Copy the dictionary. */ c = fgetc(in); while(c != EOF){ fputc(c, out); c = fgetc(in); } fprintf(stdout, "Conversion successful.\n\n"); } bool convert_tree(FILE* in, FILE* out, int end_position) { while(TRUE){ BYTE2 symbol; if(fread(&symbol, sizeof(BYTE2), 1, in) < 1){ fprintf(stderr, "Error: reached file end before tree symbol.\n\n"); return FALSE; } if(ftell(in) >= end_position){ fprintf(stderr, "Error: Tree symbol overlaps dictionary.\n\n"); return FALSE; } symbol = swap2(symbol); fwrite(&symbol, sizeof(BYTE2), 1, out); BYTE4 usage; if(fread(&usage, sizeof(BYTE4), 1, in) < 1){ fprintf(stderr, "Error: reached file end before tree usage.\n\n"); return FALSE; } if(ftell(in) >= end_position){ fprintf(stderr, "Error: Tree usage overlaps dictionary.\n\n"); return FALSE; } usage = swap4(usage); fwrite(&usage, sizeof(BYTE4), 1, out); BYTE2 count; if(fread(&count, sizeof(BYTE2), 1, in) < 1){ fprintf(stderr, "Error: reached file end before tree count.\n\n"); return FALSE; } if(ftell(in) >= end_position){ fprintf(stderr, "Error: Tree count overlaps dictionary.\n\n"); return FALSE; } count = swap2(count); fwrite(&count, sizeof(BYTE2), 1, out); BYTE2 branch; if(fread(&branch, sizeof(BYTE2), 1, in) < 1){ fprintf(stderr, "Error: reached file end before tree branch.\n\n"); return FALSE; } branch = swap2(branch); if(ftell(in) == end_position){ fwrite(&branch, sizeof(BYTE2), 1, out); break; } if(ftell(in) > end_position){ fprintf(stderr, "Error: Tree branch overlaps dictionary.\n\n"); return FALSE; } fwrite(&branch, sizeof(BYTE2), 1, out); } return TRUE; } BYTE2 swap2(BYTE2 val) { return (((val & 0xFF00) >> 8) | ((val & 0x00FF) << 8)); } BYTE4 swap4(BYTE4 val) { return (((val & 0xFF000000) >> 24) | ((val & 0x00FF0000) >> 8) | ((val & 0x0000FF00) << 8) | ((val & 0x000000FF) << 24)); }