/*
* 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));
}