diff options
author | B. Watson <urchlay@slackware.uk> | 2025-02-27 02:11:46 -0500 |
---|---|---|
committer | B. Watson <urchlay@slackware.uk> | 2025-02-27 02:11:46 -0500 |
commit | 63dc39eae5df00607a531f86841191aa30da00db (patch) | |
tree | f0ab22ba4cb60a2c1d5cc8c0b71bd30cb7a37898 /listamsb.c | |
parent | 4ecf4de44ae2bb81b1972cb8b0a5ccbe27ef9e61 (diff) | |
download | bw-atari8-tools-63dc39eae5df00607a531f86841191aa30da00db.tar.gz |
listamsb: support "locked" programs.
Diffstat (limited to 'listamsb.c')
-rw-r--r-- | listamsb.c | 105 |
1 files changed, 87 insertions, 18 deletions
@@ -57,6 +57,10 @@ it would overlap the GR.0 display list or the ROMs at $c000 */ #define MAX_PTR 0xbc1f +/* SAVE "filename" LOCK does 'encryption' by subtracting every byte + from this (except the 3-byte header) */ +#define UNLOCK_KEY 0x54 + const char *self; char pipe_command[BUFSIZ + 1] = { "a8cat" }; @@ -66,8 +70,9 @@ int raw_output = 0; /* -a */ int check_only = 0; /* -c */ int startline = 0; /* -r */ int endline = MAX_LINENO; /* -r */ +int unlock_mode = 0; /* -u */ - +int locked = 0; int need_pclose = 0; int bytes_read = 0; int warnings = 0; @@ -104,6 +109,25 @@ unsigned char read_byte(void) { return (unsigned char)c; } +/* "decrypt" a byte from a "SAVE x LOCK" program. */ +unsigned char unlock_byte(unsigned char b) { + return ((UNLOCK_KEY - b) & 0xff); +} + +/* the "encryption" is the same (process is reversible) */ +#define lock_byte(x) unlock_byte(x) + +unsigned char read_prog_byte(void) { + unsigned char b = read_byte(); + return locked ? unlock_byte(b) : b; +} + +void unread_prog_byte(unsigned char b) { + if(locked) b = lock_byte(b); + ungetc(b, infile); + bytes_read--; +} + int read_word(void) { int w; @@ -113,11 +137,23 @@ int read_word(void) { return w; } +int read_prog_word(void) { + int w; + + w = read_prog_byte(); + w |= (read_prog_byte() << 8); + + return w; +} + void read_header(void) { - unsigned char b; + /* $00 for plain, $01 for SAVE with LOCK */ + locked = read_byte(); + if(locked > 1) die2("not an AMSB file: first byte not $00 or $01"); - b = read_byte(); - if(b) die2("not an AMSB file: first byte not $00"); + if(verbose && locked) { + fprintf(stderr, "program is locked, decrypting\n"); + } proglen = read_word(); @@ -159,14 +195,14 @@ int next_line(void) { /* pointer to last token on the line, offset by whatever MEMLO happened to be when the file was SAVEd. 0 means this is the last line. */ - ptr = read_word(); + ptr = read_prog_word(); if(!ptr) { if(verbose) fprintf(stderr, "end of program\n"); return 0; } - lineno = read_word(); + lineno = read_prog_word(); if(verbose) fprintf(stderr, "found line %d, offset %d, end-of-line %d\n", lineno, offset, ptr); @@ -212,7 +248,7 @@ int next_line(void) { /* walk and print the tokens. when we hit a null byte, we're done. */ while(1) { - byte = read_byte(); + byte = read_prog_byte(); if(in_string) { if(byte == 0x00 || byte == '|') { @@ -231,11 +267,10 @@ int next_line(void) { if(printing) putc(byte, outfile); } else if(byte == ':') { /* don't print the colon if the next token is a ! or ' for a comment */ - unsigned char next = read_byte(); + unsigned char next = read_prog_byte(); if( !(next == TOK_SQUOTE || next == TOK_BANG) ) if(printing) putc(byte, outfile); - ungetc(next, infile); - bytes_read--; + unread_prog_byte(next); } else if(byte == '"') { /* strings start but *don't end* with a double-quote */ in_string = 1; @@ -300,12 +335,37 @@ int next_line(void) { return 1; } +/* when this gets called, input and output are open, read_header() + has already run. "locking" and "unlocking" are the same + transform, so this function does both. */ +void unlock_program(void) { + int c; + + fprintf(stderr, "%s: program is %slocked, output will be %slocked\n", + self, locked ? "" : "un", locked ? "un" : ""); + + /* 3-byte header: 0 for unlocked, 1 for locked */ + fputc(!locked, outfile); + /* LSB of program length (not encrypted) */ + fputc(proglen & 0xff, outfile); + /* MSB */ + fputc((proglen >> 8) & 0xff, outfile); + + /* rest of file, including trailing nulls, is transformed */ + while( (c = fgetc(infile)) >= 0) + fputc(unlock_byte(c & 0xff), outfile); + fclose(outfile); + + exit(0); +} + void print_help(void) { printf("%s v" VERSION " - detokenize Atari Microsoft BASIC files\n", self); puts("By B. Watson <urchlay@slackware.uk>, released under the WTFPL"); - printf("Usage: %s [-a] [-v] [-h] [-i] [-u] [-t] [-m] [-s] [-r *start,end*] [file]\n", self); + printf("Usage: %s [[-l] | [-a] [-v] [-h] [-i] [-u] [-t] [-m] [-s] [-r *start,end*]] [file]\n", self); puts(" -a: raw ATASCII output"); puts(" -c: check only (no listing)"); + puts(" -l: lock or unlock program"); puts(" -v: verbose"); puts(" -r: only list lines numbered from *start* to *end*"); puts(" --help, -h: print this help and exit"); @@ -364,12 +424,13 @@ void parse_args(int argc, char **argv) { } } - while( (opt = getopt(argc, argv, "r:cvaiutmsh")) != -1) { + while( (opt = getopt(argc, argv, "lr:cvaiutmsh")) != -1) { switch(opt) { - case 'c': check_only = 1; break; - case 'a': raw_output = 1; break; + case 'l': unlock_mode = 1; break; + case 'c': check_only = 1; break; + case 'a': raw_output = 1; break; + case 'v': verbose = 1; break; case 'h': print_help(); exit(0); - case 'v': verbose = 1; break; case 'r': get_line_range(optarg); break; case 'i': case 'u': @@ -413,9 +474,12 @@ void open_output() { } if(verbose) fprintf(stderr, "using /dev/null for output (check_only)\n"); - } else if(raw_output) { - if(isatty(fileno(stdout))) - die("refusing to write raw ATASCII to a terminal"); + } else if(raw_output || unlock_mode) { + if(isatty(fileno(stdout))) { + fprintf(stderr, "%s: refusing to write %s to a terminal\n", + self, (unlock_mode ? "tokenized BASIC" : "raw ATASCII")); + exit(1); + } outfile = stdout; if(verbose) fprintf(stderr, "using stdout for output\n"); @@ -442,6 +506,11 @@ int main(int argc, char **argv) { read_header(); + if(unlock_mode) { + unlock_program(); + exit(0); + } + while(next_line()) linecount++; |