diff options
| -rw-r--r-- | unprotbas.1 | 8 | ||||
| -rw-r--r-- | unprotbas.c | 113 | ||||
| -rw-r--r-- | unprotbas.rst | 6 | 
3 files changed, 95 insertions, 32 deletions
diff --git a/unprotbas.1 b/unprotbas.1 index 92e6b66..3b8b832 100644 --- a/unprotbas.1 +++ b/unprotbas.1 @@ -27,7 +27,7 @@ level margin: \\n[rst2man-indent\\n[rst2man-indent-level]]  .\" new: \\n[rst2man-indent\\n[rst2man-indent-level]]  .in \\n[rst2man-indent\\n[rst2man-indent-level]]u  .. -.TH "UNPROTBAS" 1 "2024-05-17" "0.2.1" "Urchlay's Atari 8-bit Tools" +.TH "UNPROTBAS" 1 "2024-05-18" "0.2.1" "Urchlay's Atari 8-bit Tools"  .SH NAME  unprotbas \- Unprotect LIST-protected Atari 8-bit BASIC programs  .SH SYNOPSIS @@ -44,9 +44,9 @@ read from standard input.  .sp  \fBoutput\-file\fP will be the unprotected tokenized BASIC program. If it  already exists, it will be overwritten. Use \fI\-\fP to write to standard -output, but \fB[TODO]\fP \fBunprotbas\fP will refuse to write to standard -output if it\(aqs a terminal (since tokenized BASIC is binary data and -may confuse the terminal). +output, but \fBunprotbas\fP will refuse to write to standard output if +it\(aqs a terminal (since tokenized BASIC is binary data and may confuse +the terminal).  .SH OPTIONS  .INDENT 0.0  .TP diff --git a/unprotbas.c b/unprotbas.c index 9c45fbb..70741e3 100644 --- a/unprotbas.c +++ b/unprotbas.c @@ -8,6 +8,7 @@  #include <stdio.h>  #include <unistd.h>  #include <stdlib.h> +#include <string.h>  /* attempt to fix a "list-protected" Atari 8-bit BASIC program.  	we don't fully detokenize, so this won't fix truly corrupted @@ -71,6 +72,46 @@ unsigned short getword(int addr) {  	return data[addr] | (data[addr + 1] << 8);  } +void setword(int addr, int value) { +	data[addr] = value & 0xff; +	data[addr + 1] = value >> 8; +} + +void dump_header_vars(void) { +	fprintf(stderr, "LOMEM %04x\n", lomem); +	fprintf(stderr, "VNTP %04x\n", vntp); +	fprintf(stderr, "VNTD %04x\n", vntd); +	fprintf(stderr, "VVTP %04x\n", vvtp); +	fprintf(stderr, "STMTAB %04x, codestart %04x\n", stmtab, codestart); +	fprintf(stderr, "STMCUR %04x\n", stmcur); +	fprintf(stderr, "STARP %04x\n", starp); +	fprintf(stderr, "vvstart %04x\n", vvstart); +} + +void read_header(void) { +	lomem = getword(0); +	vntp = getword(2); +	vntd = getword(4); +	vvtp = getword(6); +	stmtab = getword(8); +	stmcur = getword(10); +	starp = getword(12); +	codestart = stmtab - STM_OFFSET - (vntp - 256); +	vnstart = vntp - 256 + 14; +	vvstart = vvtp - 256 + 14; +	dump_header_vars(); +} + +void set_header_vars(void) { +	setword(0, lomem); +	setword(2, vntp); +	setword(4, vntd); +	setword(6, vvtp); +	setword(8, stmtab); +	setword(10, stmcur); +	setword(12, starp); +} +  /* fixline() calculates & sets correct line length, by iterating  	over the statement(s) within the line. the last statement's  	offset will be the same as the line offset should have been, @@ -157,6 +198,21 @@ int fixcode(void) {  	return result;  } +/* sometimes the variable name table isn't large enough to hold +	the generated variable names. move_code_up() makes more space, +	by moving the rest of the program (including the variable value +	table) up in memory. */ +void move_code_up(int offset) { +	memmove(data + vvstart + offset, data + vvstart, filelen); +	vvtp += offset; +	stmtab += offset; +	stmcur += offset; +	starp += offset; +	set_header_vars(); +	read_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 @@ -171,9 +227,10 @@ int fixcode(void) {  	the file.  	We can find the actual table size in the file by subtracting VNTP -	(start of variable name table) from VNTD (end of variable name table), -	and if we run out of space for the generated names, something is -	seriously off... +	(start of variable name table) from VNTD (end of variable name table). +	It's possible that the table size is too small for the generated +	variable names, in which case we have to call move_code_up() to +	make more room.  	The maximum number of variable names is 128. If all 128 vars are in  	use, the minimum table size is 230 (26 one-letter names, 102 2-letter @@ -214,7 +271,9 @@ int fixvars(void) {  	vp = vnstart;  	while(vp < vvstart) {  		unsigned char c = data[vp]; +		/*  		fprintf(stderr, "%04x/%04x: %04x\n", vp, vvstart, c); +		*/  		/* allow a null byte only at the end of the table! */  		/* if(c == 0 && vp == vvstart - 1) break; */ @@ -237,7 +296,26 @@ int fixvars(void) {  	}  	if(!forcevars && !bad) return 0; +	/* decide whether we have enough room. pretend every new variable name +		is 3 bytes (really only true for the 10th and later strings and +		arrays, but a little wasted space won't hurt anything). */ +	{ +		int vntblsize = vvstart - vnstart; +		int varcount = (codestart - vvstart) / 8; +		int neededsize = varcount * 3 + 1; +		int move_up_by; + +		fprintf(stderr, "%d variables according to value table\n", varcount); +		if(neededsize > vntblsize) { +			move_up_by = neededsize - vntblsize; +			fprintf(stderr, "need %d bytes for vntable, only have %d, moving up by %d to %04x\n", +					neededsize, vntblsize, move_up_by, vvtp + move_up_by); +			move_code_up(move_up_by); +		} +	} +  	vp = vnstart; +	vv = vvstart;  	while(vv < codestart) {  		unsigned char sigil = 0;  		/* type: scalar = 0, array = 1, string = 2 */ @@ -259,8 +337,8 @@ int fixvars(void) {  			data[vp] = ('A' + varname);  		} else {  			varname -= 26; -			data[vp++] = 'A' + (varname / 9); -			data[vp] = ('1' + (varname % 9)); +			data[vp++] = 'A' + ((varname - 26) / 9); +			data[vp] = ('1' + ((varname - 26) % 9));  		}  		if(sigil) { @@ -327,6 +405,10 @@ void open_input(const char *name) {  void open_output(const char *name) {  	if(!name) { +		if(isatty(fileno(stdout))) { +			fprintf(stderr, "%s: refusing to write binary data to standard output\n", self); +			exit(1); +		}  		if(freopen(NULL, "wb", stdout)) {  			output_file = stdout;  			return; @@ -381,29 +463,10 @@ int main(int argc, char **argv) {  	parse_args(argc, argv);  	filelen = readfile(); - -	lomem = getword(0); -	vntp = getword(2); -	vntd = getword(4); -	vvtp = getword(6); -	stmtab = getword(8); -	stmcur = getword(10); -	starp = getword(12); -	codestart = stmtab - STM_OFFSET - (vntp - 256); -	vnstart = vntp - 256 + 14; -	vvstart = vvtp - 256 + 14; +	read_header();  	if(lomem) die("This doesn't look like an Atari BASIC program (no $0000 signature)"); -	fprintf(stderr, "LOMEM %04x\n", lomem); -	fprintf(stderr, "VNTP %04x\n", vntp); -	fprintf(stderr, "VNTD %04x\n", vntd); -	fprintf(stderr, "VVTP %04x\n", vvtp); -	fprintf(stderr, "STMTAB %04x, codestart %04x\n", stmtab, codestart); -	fprintf(stderr, "STMCUR %04x\n", stmcur); -	fprintf(stderr, "STARP %04x\n", starp); -	fprintf(stderr, "vvstart %04x\n", vvstart); -  	/*  	fprintf(stderr, "data at STMTAB (we hope):\n");  	for(int i=codestart; i<filelen; i++) { diff --git a/unprotbas.rst b/unprotbas.rst index 735681e..557d27d 100644 --- a/unprotbas.rst +++ b/unprotbas.rst @@ -25,9 +25,9 @@ read from standard input.  **output-file** will be the unprotected tokenized BASIC program. If it  already exists, it will be overwritten. Use *-* to write to standard -output, but **[TODO]** **unprotbas** will refuse to write to standard -output if it's a terminal (since tokenized BASIC is binary data and -may confuse the terminal). +output, but **unprotbas** will refuse to write to standard output if +it's a terminal (since tokenized BASIC is binary data and may confuse +the terminal).  OPTIONS  =======  | 
