aboutsummaryrefslogtreecommitdiff
path: root/listamsb.c
diff options
context:
space:
mode:
Diffstat (limited to 'listamsb.c')
-rw-r--r--listamsb.c105
1 files changed, 87 insertions, 18 deletions
diff --git a/listamsb.c b/listamsb.c
index 207c3bb..6737243 100644
--- a/listamsb.c
+++ b/listamsb.c
@@ -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++;