diff options
| -rw-r--r-- | Makefile | 4 | ||||
| -rw-r--r-- | bas.c | 91 | ||||
| -rw-r--r-- | bas.h | 7 | ||||
| -rw-r--r-- | protbas.c | 195 | ||||
| -rw-r--r-- | unprotbas.c | 283 | 
5 files changed, 317 insertions, 263 deletions
| @@ -16,7 +16,7 @@ CC=gcc  CFLAGS=-Wall $(COPT) -ansi -D_GNU_SOURCE -DVERSION=\"$(VERSION)\"  # BINS and SCRIPTS go in $BINDIR, DOCS go in $DOCDIR -BINS=a8eol xfd2atr atr2xfd blob2c cart2xex fenders xexsplit xexcat atrsize rom2cart unmac65 axe blob2xex xexamine xex1to2 unprotbas +BINS=a8eol xfd2atr atr2xfd blob2c cart2xex fenders xexsplit xexcat atrsize rom2cart unmac65 axe blob2xex xexamine xex1to2 unprotbas protbas  SCRIPTS=dasm2atasm a8utf8  MANS=a8eol.1 xfd2atr.1 atr2xfd.1 blob2c.1 cart2xex.1 fenders.1 xexsplit.1 xexcat.1 atrsize.1 rom2cart.1 unmac65.1 axe.1 dasm2atasm.1 a8utf8.1 blob2xex.1 xexamine.1 xex1to2.1 unprotbas.1  MAN5S=xex.5 @@ -51,6 +51,8 @@ all: $(BINS) manpages symlinks subdirs  unprotbas: bas.o +protbas: bas.o +  bas.o: bas.c bas.h  subdirs: @@ -27,24 +27,50 @@ const char *self;  unsigned char program[BUFSIZE];  FILE *input_file;  FILE *output_file; +char *output_filename = NULL;  void die(const char *msg) {  	fprintf(stderr, "%s: %s\n", self, msg);  	exit(1);  } +void parse_general_args(int argc, char **argv, void (*helpfunc)()) { +	if(argc < 2) { +		(*helpfunc)(); +		exit(1); +	} + +	if(strcmp(argv[1], "--help") == 0) { +		(*helpfunc)(); +		exit(0); +	} + +	if(strcmp(argv[1], "--version") == 0) { +		printf("%s %s\n", self, VERSION); +		exit(0); +	} +} +  /* read entire file into memory */ -int readfile(void) { -	int got = fread(program, 1, BUFSIZE - 1, input_file); -	if(verbose) fprintf(stderr, "Read %d bytes.\n", got); +void readfile(void) { +	filelen = fread(program, 1, BUFSIZE - 1, input_file); +	if(verbose) fprintf(stderr, "Read %d bytes.\n", filelen);  	if(!feof(input_file))  		fprintf(stderr, "Warning: file is >64KB, way too big for a BASIC program.\n"); -	else if(got > MAX_PROG_SIZE) -		fprintf(stderr, "Warning: file is %d bytes, suspiciously large for a BASIC program.\n", got); +	else if(filelen > MAX_PROG_SIZE) +		fprintf(stderr, "Warning: file is %d bytes, suspiciously large for a BASIC program.\n", filelen);  	fclose(input_file); -	if(got < MIN_PROG_SIZE) +	if(filelen < MIN_PROG_SIZE)  		die("File too short to be a BASIC program (truncated?)\n"); -	return got; +} + +int writefile(void) { +	int outbytes; + +	outbytes = fwrite(program, 1, filelen, output_file); +	fclose(output_file); +	if(verbose) fprintf(stderr, "Wrote %d bytes.\n", outbytes); +	return outbytes;  }  /* get a 16-bit value from the file, in 6502 LSB/MSB order. */ @@ -76,6 +102,8 @@ void parse_header(void) {  	vvstart = vvtp - TBL_OFFSET;  	code_end = starp - TBL_OFFSET; +	if(lomem) die("This doesn't look like an Atari BASIC program (no $0000 signature)."); +  	if(filelen < code_end) {  		fprintf(stderr, "Warning: file is truncated: %d bytes, should be %d.\n", filelen, code_end);  	} @@ -111,9 +139,9 @@ void move_code(int offset) {  	stmtab += offset;  	stmcur += offset;  	starp += offset; +	filelen += offset;  	update_header();  	parse_header(); -	filelen += offset;  }  void adjust_vntable_size(int oldsize, int newsize) { @@ -127,6 +155,53 @@ void adjust_vntable_size(int oldsize, int newsize) {  	}  } +/* return true if the variable name table is OK */ +int vntable_ok(void) { +	int vp, bad; + +	if(vntp == vntd) { +		if(verbose) fprintf(stderr, "No variables.\n"); +		return 1; +	} + +	/* first pass: bad = 1 if all the bytes in the table have the same +		value, no matter what it is. */ +	vp = vnstart + 1; +	bad = 1; +	while(vp < vvstart - 1) { +		if(program[vp] != program[vnstart]) { +			bad = 0; +			break; +		} +		vp++; +	} +	if(bad) return 0; + +	/* 2nd pass: bad = 1 if there's any invalid character in the table. */ +	vp = vnstart; +	while(vp < vvstart) { +		unsigned char c = program[vp]; + +		/* treat a null byte as end-of-table, ignore any junk between it and VNTP. */ +		if(c == 0) break; + +		vp++; + +		/* inverse $ or ( is OK */ +		if(c == 0xa4 || c == 0xa8) continue; + +		/* numbers and letters are allowed, inverse or normal. */ +		c &= 0x7f; +		if(c >= 0x30 && c <= 0x39) continue; +		if(c >= 0x41 && c <= 0x5a) continue; + +		bad++; +		break; +	} + +	return !bad; +} +  void invalid_args(const char *arg) {  	fprintf(stderr, "%s: Invalid argument '%s'.\n\n", self, arg);  	exit(1); @@ -62,11 +62,15 @@ extern unsigned char program[BUFSIZE];  extern FILE *input_file;  extern FILE *output_file; +extern char *output_filename; +  extern int verbose;  extern void set_self(const char *argv0);  extern void die(const char *msg); -extern int readfile(void); +extern void parse_general_args(int argc, char **argv, void (*helpfunc)()); +extern int writefile(void); +extern void readfile(void);  extern unsigned short getword(int addr);  extern void setword(int addr, int value);  extern void dump_header_vars(void); @@ -74,6 +78,7 @@ extern void parse_header(void);  extern void update_header(void);  extern void move_code(int offset);  extern void adjust_vntable_size(int oldsize, int newsize); +extern int vntable_ok(void);  extern void invalid_args(const char *arg);  extern FILE *open_file(const char *name, const char *mode);  extern void open_input(const char *name); diff --git a/protbas.c b/protbas.c new file mode 100644 index 0000000..e57ead3 --- /dev/null +++ b/protbas.c @@ -0,0 +1,195 @@ +#include <stdio.h> +#include <unistd.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> +#include <time.h> + +#include "bas.h" + +int protect_vars = 1; +int protect_code = 1; +int shrinktable  = 0; +int varname_char = 0x9b; + +/* 32767 END */ +unsigned char badcode[] = { +	0xff, 0x7f,   /* line number 32767      */ +	0x00,         /* *bad* next-line offset */ +	0x06,         /* next-statement offset  */ +	0x15,         /* END token              */ +	0x16,         /* end-of-line token      */ +}; + +void scramble_vars(void) { +	int i; + +	if(!vntable_ok()) { +		fprintf(stderr, "%s: Program already was variable-protected.\n", self); +		exit(2); +	} + +	if(shrinktable) { +		if(verbose) fprintf(stderr, "Shrinking variable name table.\n"); +		adjust_vntable_size((vvstart - 1) - vnstart, (codestart - vvstart) / 8); +	} + +	if(varname_char == -1) srand(time(NULL)); + +	for(i = vnstart; i < vvstart - 1; i++) +		if(varname_char == -1) +			program[i] = (rand() >> 8) & 0xff; +		else +			program[i] = varname_char & 0xff; + +	if(verbose) { +		i -= vnstart; +		if(i) { +			fprintf(stderr, "Replaced %d byte variable name table with ", i); +			if(varname_char == -1) +				fprintf(stderr, "random characters.\n"); +			else +				fprintf(stderr, "character $%02x.\n", varname_char); +		} else { +			fprintf(stderr, "Can't protect variables because there are no variables.\n"); +		} +	} +} + +/* iterate over all the lines, insert a poisoned line 32767 just +	before line 32768 */ +void breakcode(void) { +	int pos = codestart, oldpos = 0; +	int offset, lineno = -1, tmpno = -1; + +	while(pos < filelen) { +		lineno = tmpno; +		tmpno = getword(pos); +		if(tmpno == 32768) { +			break; +		} else { +			offset = program[pos + 2]; +			if(!offset) { +				fprintf(stderr, "%s: program already was code-protected.\n", self); +				exit(2); +			} +			oldpos = pos; +			pos += offset; +		} +	} + +	if(!oldpos) die("Can't protect code because there are no lines of code."); +	if(lineno == 32767) die("Can't protect code because there is already a line 32767."); + +	/* pos is now the start of line 32768, move it up to make room for +		the new line */ +	offset = sizeof(badcode); +	memmove(program + pos + offset, program + pos, filelen); + +	/* insert new line */ +	memmove(program + pos, badcode, offset); + +	if(verbose) +		fprintf(stderr, "Inserted line 32767 with invalid offset at file offset $%04x.\n", pos); + +	/* update pointers that would be affected by the code move */ +	stmcur += offset; +	starp += offset; +	filelen += offset; +	update_header(); +	parse_header(); +} + +void print_help(void) { +	fprintf(stderr, "Usage: %s [-v] [-nc|-nv] [-s] [-x[r|NN]] <inputfile> <outputfile>\n", self); +	fprintf(stderr, "  -v: Verbose.\n"); +	fprintf(stderr, " -nc: Don't protect code.\n"); +	fprintf(stderr, " -nv: Don't protect variable names.\n"); +	fprintf(stderr, "  -s: Shrink variable name table to min size.\n"); +	fprintf(stderr, "-xNN: Hex code NN for variable names.\n"); +	fprintf(stderr, " -xr: Random variable names.\n"); +	fprintf(stderr, "Use - as a filename to read from stdin and/or write to stdout.\n"); +} + +void parse_args(int argc, char **argv) { +	int opt, xopt_used = 0; + +	while( (opt = getopt(argc, argv, "vn:x:s")) != -1) { +		switch(opt) { +			case 'v': verbose = 1; break; +			case 's': shrinktable = 1; break; +			case 'n': +				switch(optarg[0]) { +					case 'c': protect_code = 0; break; +					case 'v': protect_vars = 0; break; +					default: +						die("Invalid argument for -n (must be 'c' or 'v')."); +				} +				break; +			case 'x': +				xopt_used = 1; +				switch(optarg[0]) { +					case 'r': +						varname_char = -1; break; +					case 0: +						die("-x option requires a hex number or 'r'."); break; +					default: +						{ +							char *e; +							varname_char = (int)strtol(&(*argv)[2], &e, 16); +							if(*e != 0 || varname_char > 0xff) +								die("Invalid hex value for -x option (range is 0 to ff)."); +						} +				} +				break; +			default: +				print_help(); +				exit(1); +		} +	} + +	if(!protect_code && !protect_vars) { +		die("Nothing to do: -nc and -nv both given."); +	} + +	if(!protect_vars) { +		if(xopt_used) +			die("-x option not valid with -nv."); +		if(shrinktable) +			die("-s option not valid with -nv."); +	} + +	if(optind >= argc) +		die("No input file given (use - for stdin)."); +	else +		open_input(argv[optind]); + +	if(++optind >= argc) +		die("No output file given (use - for stdout)."); +	else +		output_filename = argv[optind]; +} + +int main(int argc, char **argv) { +	set_self(*argv); +	parse_general_args(argc, argv, print_help); +	parse_args(argc, argv); +	readfile(); +	parse_header(); + +	if(verbose) { +		fprintf(stderr, "Protecting program, "); +		if(protect_vars && !protect_code) +			fprintf(stderr, "variables only.\n"); +		else if(protect_code && !protect_vars) +			fprintf(stderr, "code only.\n"); +		else +			fprintf(stderr, "both code and variables.\n"); +	} +	if(protect_vars) scramble_vars(); +	if(protect_code) breakcode(); + +	open_output(output_filename); +	writefile(); +	return 0; /* TODO: meaningful return status */ +} diff --git a/unprotbas.c b/unprotbas.c index 8c5c14a..5bde236 100644 --- a/unprotbas.c +++ b/unprotbas.c @@ -18,21 +18,6 @@  	   or whatever), we "fix" that by making up new variable names.  */ -/* for the -p/-pc options: 32767 END */ -unsigned char badcode[] = { -	0xff, 0x7f,   /* line number 32767      */ -	0x00,         /* *bad* next-line offset */ -	0x06,         /* next-statement offset  */ -	0x15,         /* END token              */ -	0x16,         /* end-of-line token      */ -}; - -/* for -p/-pv */ -int varname_char = 0x9b; - -/* for -s */ -int shrinktable = 0; -  /* for the -r option */  #define MAP_FILE "varnames.txt"  unsigned char varnames[BUFSIZE]; @@ -47,10 +32,6 @@ int checkonly = 0;  int was_protected = 0;  int readmap = 0;  int writemap = 0; -int protect_vars = 0; -int protect_code = 0; - -char *output_filename = NULL;  /* fixline() calculates & sets correct line length, by iterating  	over the statement(s) within the line. the last statement's @@ -149,50 +130,6 @@ int fixcode(void) {  	return result;  } -/* iterate over all the lines, insert a poisoned line 32767 just -	before line 32768 */ -void breakcode(void) { -	int pos = codestart, oldpos = 0; -	int offset, lineno = -1, tmpno = -1; - -	while(pos < filelen) { -		lineno = tmpno; -		tmpno = getword(pos); -		if(tmpno == 32768) { -			break; -		} else { -			offset = program[pos + 2]; -			if(!offset) { -				fprintf(stderr, "%s: program already was code-protected.\n", self); -				exit(2); -			} -			oldpos = pos; -			pos += offset; -		} -	} - -	if(!oldpos) die("Can't protect code because there are no lines of code."); -	if(lineno == 32767) die("Can't protect code because there is already a line 32767."); - -	/* pos is now the start of line 32768, move it up to make room for -		the new line */ -	offset = sizeof(badcode); -	memmove(program + pos + offset, program + pos, filelen); - -	/* insert new line */ -	memmove(program + pos, badcode, offset); - -	if(verbose) -		fprintf(stderr, "Inserted line 32767 with invalid offset at file offset $%04x.\n", pos); - -	/* update pointers that would be affected by the code move */ -	stmcur += offset; -	starp += offset; -	update_header(); -	parse_header(); -	filelen += offset; -} -  /* Fixing the variables is a bit more work than it seems like  	it might be, because the last byte of the name has to match  	the type (inverse video "(" for numeric array, inverse "$" for @@ -217,53 +154,6 @@ void breakcode(void) {  	or letter+number or one-letter string/array names).  */ -/* return true if the variable name table is OK */ -int vntable_ok(void) { -	int vp, bad; - -	if(vntp == vntd) { -		if(verbose) fprintf(stderr, "No variables.\n"); -		return 1; -	} - -	/* first pass: bad = 1 if all the bytes in the table have the same -		value, no matter what it is. */ -	vp = vnstart + 1; -	bad = 1; -	while(vp < vvstart - 1) { -		if(program[vp] != program[vnstart]) { -			bad = 0; -			break; -		} -		vp++; -	} -	if(bad) return 0; - -	/* 2nd pass: bad = 1 if there's any invalid character in the table. */ -	vp = vnstart; -	while(vp < vvstart) { -		unsigned char c = program[vp]; - -		/* treat a null byte as end-of-table, ignore any junk between it and VNTP. */ -		if(c == 0) break; - -		vp++; - -		/* inverse $ or ( is OK */ -		if(c == 0xa4 || c == 0xa8) continue; - -		/* numbers and letters are allowed, inverse or normal. */ -		c &= 0x7f; -		if(c >= 0x30 && c <= 0x39) continue; -		if(c >= 0x41 && c <= 0x5a) continue; - -		bad++; -		break; -	} - -	return !bad; -} -  /* walk the variable value table, generating variable names.  	if write is 0, just return the size the table will be.  	if write is 1, actually write the names to memory. */ @@ -495,44 +385,8 @@ void apply_var_map(void) {  	memmove(program + vnstart, new_vntable, newp);  } -void scramble_vars(void) { -	int i; - -	if(!vntable_ok()) { -		fprintf(stderr, "%s: Program already was variable-protected.\n", self); -		exit(2); -	} - -	if(shrinktable) { -		if(verbose) fprintf(stderr, "Shrinking variable name table.\n"); -		adjust_vntable_size((vvstart - 1) - vnstart, (codestart - vvstart) / 8); -	} - -	if(varname_char == -1) srand(time(NULL)); - -	for(i = vnstart; i < vvstart - 1; i++) -		if(varname_char == -1) -			program[i] = (rand() >> 8) & 0xff; -		else -			program[i] = varname_char & 0xff; - -	if(verbose) { -		i -= vnstart; -		if(i) { -			fprintf(stderr, "Replaced %d byte variable name table with ", i); -			if(varname_char == -1) -				fprintf(stderr, "random characters.\n"); -			else -				fprintf(stderr, "character $%02x.\n", varname_char); -		} else { -			die("Can't protect variables because there are no variables."); -		} -	} -} -  void print_help(void) {  	fprintf(stderr, "Usage: %s [-v] [-f] [-n] [-g] [-c] [-r|-w]  <inputfile> <outputfile>\n", self); -	fprintf(stderr, "       %s [-v] [-p|-pc|-pv] [-xr|-xNN] [-s] <inputfile> <outputfile>\n", self);  	fprintf(stderr, "  -v: Verbose.\n");  	fprintf(stderr, "  -f: Force variable name table rebuild.\n");  	fprintf(stderr, "  -n: Do not rebuild variable name table, even if it's invalid.\n"); @@ -540,32 +394,13 @@ void print_help(void) {  	fprintf(stderr, "  -c: Check only; no output file.\n");  	fprintf(stderr, "  -w: Write variable names to 'varnames.txt'.\n");  	fprintf(stderr, "  -r: Read variable names from 'varnames.txt'.\n"); -	fprintf(stderr, "  -pc/-pv/-p: Protect code/variables/both.\n"); -	fprintf(stderr, "  -s: Shrink variable name table to min size, with -p/-pv.\n"); -	fprintf(stderr, "  -xNN: Hex code NN for variable names, with -p/-pv.\n"); -	fprintf(stderr, "  -xr: Random variable names, with -p/-pv.\n");  	fprintf(stderr, "Use - as a filename to read from stdin and/or write to stdout.\n");  }  void parse_args(int argc, char **argv) { -	int xopt_used = 0; -  	set_self(*argv); -	if(argc < 2) { -		print_help(); -		exit(1); -	} - -	if(strcmp(argv[1], "--help") == 0) { -		print_help(); -		exit(0); -	} - -	if(strcmp(argv[1], "--version") == 0) { -		printf("%s %s\n", self, VERSION); -		exit(0); -	} +	parse_general_args(argc, argv, print_help);  	while(++argv, --argc) {  		if((*argv)[0] == '-') { @@ -577,37 +412,6 @@ void parse_args(int argc, char **argv) {  				case 'c': checkonly = 1; break;  				case 'r': readmap = 1; break;  				case 'w': writemap = 1; break; -				case 'p': { -								 switch((*argv)[2]) { -									 case 'c': -										 protect_code = 1; break; -									 case 'v': -										 protect_vars = 1; break; -									 case 0: -										 protect_code = protect_vars = 1; break; -									 default: -										 die("Invalid -p suboption (only -p, -pc, -pv are valid)."); -								 } -							 } -							 break; -				case 'x': { -								 xopt_used++; -								 switch((*argv)[2]) { -									case 'r': -										varname_char = -1; break; -									case 0: -										die("-x option requires a hex number or 'r' (e.g. -x20, not -x 20)."); break; -									default: -										{ -											char *e; -											varname_char = (int)strtol(&(*argv)[2], &e, 16); -											if(*e != 0 || varname_char > 0xff) -												die("invalid hex value for -x option (range is 0 to ff)."); -										} -								 } -							 } -							 break; -				case 's': shrinktable = 1; break;  				case 0:  							 if(!input_file)  								 open_input(NULL); @@ -635,80 +439,53 @@ void parse_args(int argc, char **argv) {  	if(readmap && writemap) die("-r and -w are mutually exclusive.");  	if(readmap && keepvars) die("-r and -n are mutually exclusive, maybe you want -w?");  	if(checkonly && (readmap || writemap)) die("-c and -r/-w are mutually exclusive."); -	if(protect_code || protect_vars) { -		if(checkonly || keepvars || forcevars || readmap || writemap || !keepgarbage) -			die("-p, -pc, -pv options can only be combined with -v, -x, -s."); -	} -	if(xopt_used && !protect_vars) -		die("-x option requires -p or -pv."); -	if(shrinktable && !protect_vars) -		die("-s option requires -p or -pv.");  }  int main(int argc, char **argv) { -	int outbytes, invoffs = 0; +	int invoffs = 0;  	parse_args(argc, argv); -	filelen = readfile(); +	readfile();  	parse_header(); -	if(lomem) die("This doesn't look like an Atari BASIC program (no $0000 signature)."); - -	if(protect_code || protect_vars) { -		if(verbose) { -			fprintf(stderr, "Protecting program, "); -			if(protect_vars && !protect_code) -				fprintf(stderr, "variables only.\n"); -			else if(protect_code && !protect_vars) -				fprintf(stderr, "code only.\n"); -			else -				fprintf(stderr, "both code and variables.\n"); -		} -		if(protect_vars) scramble_vars(); -		if(protect_code) breakcode(); -		was_protected = 1; /* opposite sense for this one */ +	if(readmap) { +		was_protected = !vntable_ok(); +		read_var_map(); +		apply_var_map();  	} else { -		if(readmap) { -			was_protected = !vntable_ok(); -			read_var_map(); -			apply_var_map(); -		} else { -			if(!keepvars) { -				if(fixvars()) { -					was_protected = 1; -					if(verbose) fprintf(stderr, "Variable names replaced.\n"); -				} else { -					if(verbose) fprintf(stderr, "Variable names were already OK.\n"); -				} +		if(!keepvars) { +			if(fixvars()) { +				was_protected = 1; +				if(verbose) fprintf(stderr, "Variable names replaced.\n"); +			} else { +				if(verbose) fprintf(stderr, "Variable names were already OK.\n");  			}  		} +	} -		invoffs = fixcode(); -		if(invoffs) { -			if(verbose) -				fprintf(stderr, "Fixed %d invalid offset%s in code.\n", -						invoffs, (invoffs == 1 ? "" : "s")); -			was_protected = 1; -		} else { -			if(verbose) fprintf(stderr, "No invalid offsets.\n"); -		} +	invoffs = fixcode(); +	if(invoffs) { +		if(verbose) +			fprintf(stderr, "Fixed %d invalid offset%s in code.\n", +					invoffs, (invoffs == 1 ? "" : "s")); +		was_protected = 1; +	} else { +		if(verbose) fprintf(stderr, "No invalid offsets.\n"); +	} -		if(verbose) { -			fprintf(stderr, "Program was %sprotected.\n", (was_protected ? "" : "NOT ")); -		} +	if(verbose) { +		fprintf(stderr, "Program was %sprotected.\n", (was_protected ? "" : "NOT ")); +	} -		if(checkonly) { -			if(verbose) fprintf(stderr, "Check-only mode; no output written.\n"); -			return was_protected ? 0 : 2; -		} +	if(checkonly) { +		if(verbose) fprintf(stderr, "Check-only mode; no output written.\n"); +		return was_protected ? 0 : 2;  	}  	/* we don't open the output file until all processing is done, to  		avoid leaving invalid output files if we exit on error. */  	open_output(output_filename); -	outbytes = fwrite(program, 1, filelen, output_file); -	fclose(output_file); -	if(verbose) fprintf(stderr, "Wrote %d bytes.\n", outbytes); +	writefile();  	if(writemap) write_var_map(); | 
