/* *  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 <http://www.gnu.org/licenses/>. */#include <stdio.h>#include <string.h>#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, "<ERROR>") == 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));}
