From a1144de8efebc414c33a0df9b8fccd758a242586 Mon Sep 17 00:00:00 2001 From: "B. Watson" Date: Wed, 22 May 2024 05:24:11 -0400 Subject: unprotbas: implement -r option (needs more testing though). --- unprotbas.c | 187 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 178 insertions(+), 9 deletions(-) (limited to 'unprotbas.c') diff --git a/unprotbas.c b/unprotbas.c index 9964d39..5ad483b 100644 --- a/unprotbas.c +++ b/unprotbas.c @@ -2,6 +2,7 @@ #include #include #include +#include /* attempt to fix a "list-protected" Atari 8-bit BASIC program. we don't fully detokenize, so this won't fix truly corrupted @@ -22,9 +23,16 @@ can't process. */ #define MIN_PROG_SIZE 21 +#define MAP_FILE "varnames.txt" + /* entire file gets read into memory (for now) */ unsigned char data[65536]; +/* for the -r option */ +unsigned char varnames[65536]; +unsigned char *varmap[128]; +int varmap_count; + /* BASIC 14-byte header values */ unsigned short lomem; unsigned short vntp; @@ -51,6 +59,8 @@ int keepgarbage = 1; int checkonly = 0; int was_protected = 0; int verbose = 0; +int readmap = 0; +int writemap = 0; /* file handles */ FILE *input_file = NULL; @@ -306,9 +316,8 @@ int rebuild_vntable(int write) { /* fprintf(stderr, "%04x: %04x, %d\n", vv, data[vv], type); */ if(varnum != data[vv+1]) { - fprintf(stderr, "Warning: variable value is corrupt!\n"); + fprintf(stderr, "Warning: variable #%d value is corrupt!\n", varnum); } - varnum++; switch(type) { case 1: varname = arrays++; sigil = 0xa8; break; @@ -338,6 +347,7 @@ int rebuild_vntable(int write) { } vv += 8; + varnum++; } /* there's supposed to be a null byte at the end of the table, unless @@ -381,13 +391,160 @@ int fixvars(void) { return 1; } +void write_var_map(void) { + FILE *f; + int vp, count = 0; + + if(verbose) fprintf(stderr, "Writing variable names to " MAP_FILE "\n"); + f = fopen(MAP_FILE, "w"); + if(!f) { + perror(MAP_FILE); + die("Can't create map file for -w option."); + } + + for(vp = vnstart; (vp < vntd) && (data[vp] != 0); vp++) { + unsigned char c = data[vp]; + if(c < 0x80) { + fputc(c, f); + } else { + fputc(c & 0x7f, f); + fputc('\n', f); + count++; + } + } + + fclose(f); + + if(verbose) fprintf(stderr, "Wrote %d variable names to " MAP_FILE "\n", count); +} + +void die_mapfile(char *msg, int num) { + fprintf(stderr, MAP_FILE ": line %d: %s.\n", num, msg); + exit(1); +} + +void check_varname(const unsigned char *name, int line) { + int len = strlen((char *)name); + int i; + unsigned char c = 0, type; + + /* fprintf(stderr, "check_varname(\"%s\", %d)\n", name, line); */ + + if(len < 1) die_mapfile("blank variable name", line); + if(len > 128) die_mapfile("variable name >128 characters", line); + if(name[0] < 'A' || name[0] > 'Z') + die_mapfile("invalid variable name (first character must be a letter)", line); + + for(i = 1; i < len; i++) { + c = name[i]; + if(c >= 'A' && c <= 'Z') continue; + if(c >= '0' && c <= '9') continue; + if(i == (len - 1) && ((c == '$') || (c == '('))) continue; + die_mapfile("invalid character in variable name", line); + } + + if(c == 0) c = name[0]; + + /* c now has the last char of the name, make sure it matches the variable type */ + type = data[vvstart + 8 * (line - 1)] >> 6; + /* type: scalar = 0, array = 1, string = 2 */ + if(type == 0) { + if(c == '$') + die_mapfile("type mismatch: numeric variable may not end with $", line); + else if(c == '(') + die_mapfile("type mismatch: numeric variable may not end with (", line); + } else if(type == 1) { + if(c != '(') + die_mapfile("type mismatch: array variable must end with (", line); + } else if(type == 2) { + if(c != '$') + die_mapfile("type mismatch: string variable must end with $", line); + } else { + fprintf(stderr, "Warning: variable value table is corrupt (invalid type)\n"); + } + + /* check for dups */ + for(i = 0; i < line - 1; i++) { + if(strcmp((char *)name, (char *)varmap[i]) == 0) + die_mapfile("duplicate variable name", line); + } +} + +void read_var_map(void) { + FILE *f; + unsigned char *p = varnames, *curname = varnames; + int count = 0, vvcount = (codestart - vvstart) / 8; + + if(verbose) fprintf(stderr, "Reading variable names from " MAP_FILE "\n"); + f = fopen(MAP_FILE, "r"); + if(!f) { + perror(MAP_FILE); + die("Can't read map file for -r option."); + } + + while(!feof(f)) { + *p = toupper(fgetc(f)); /* allow lowercase */ + + if(*p == ' ' || *p == '\t' || *p == '\r') + continue; /* ignore whitespace */ + + if(*p == '\n') { + *p = '\0'; + varmap[count++] = curname; + check_varname(curname, count); + curname = p + 1; + } + p++; + } + + if(verbose) fprintf(stderr, "Read %d variable names from " MAP_FILE "\n", count); + + if(vvcount > count) { + fprintf(stderr, MAP_FILE ": not enough variables (have %d, need %d).\n", count, vvcount); + exit(1); + } else if(count > vvcount) { + fprintf(stderr, MAP_FILE ": too many variables (have %d, need %d).\n", count, vvcount); + exit(1); + } + #if 0 + for(count = 0; varmap[count] != NULL; count++) { + fprintf(stderr, "\t%02d %s\n", count, varmap[count]); + } + #endif + + varmap_count = count; +} + +void apply_var_map(void) { + unsigned char new_vntable[65536]; + int i, newp = 0; + unsigned char *v; + + for(i = 0; i < varmap_count; i++) { + v = varmap[i]; + while(*v) { + new_vntable[newp++] = *v; + v++; + } + new_vntable[newp - 1] |= 0x80; + } + + if(varmap_count < 128) new_vntable[newp++] = '\0'; + + i = vvstart - vnstart; + adjust_vntable_size(i, newp); + memmove(data + vnstart, new_vntable, newp); +} + void print_help(void) { - fprintf(stderr, "Usage: %s [-v] [-f] [-n] [-g] \n", self); + fprintf(stderr, "Usage: %s [-v] [-f] [-n] [-g] [-c] [-r|-w] \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"); fprintf(stderr, "-g: remove trailing garbage, if present\n"); 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, "Use - as a filename to read from stdin and/or write to stdout\n"); } @@ -451,6 +608,8 @@ void parse_args(int argc, char **argv) { case 'n': keepvars++; break; case 'g': keepgarbage = 0; break; case 'c': checkonly = 1; break; + case 'r': readmap = 1; break; + case 'w': writemap = 1; break; case 0: if(!input_file) open_input(NULL); @@ -474,6 +633,8 @@ void parse_args(int argc, char **argv) { if(!input_file) die("no input file given (use - for stdin)"); if(!checkonly && !output_file) die("no output file given (use - for stdout)"); if(keepvars && forcevars) die("-f and -n are mutually exclusive"); + if(readmap && writemap) die("-r and -w are mutually exclusive"); + if(readmap && keepvars) die("-r and -n are mutually exclusive (maybe you want -w?)"); } int main(int argc, char **argv) { @@ -484,12 +645,18 @@ int main(int argc, char **argv) { if(lomem) die("This doesn't look like an Atari BASIC program (no $0000 signature)"); - 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(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"); + } } } @@ -519,5 +686,7 @@ int main(int argc, char **argv) { fclose(output_file); if(verbose) fprintf(stderr, "wrote %d bytes\n", got); + if(writemap) write_var_map(); + return 0; } -- cgit v1.2.3