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(); |