aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile4
-rw-r--r--bas.c91
-rw-r--r--bas.h7
-rw-r--r--protbas.c195
-rw-r--r--unprotbas.c283
5 files changed, 317 insertions, 263 deletions
diff --git a/Makefile b/Makefile
index ff04def..afd7b55 100644
--- a/Makefile
+++ b/Makefile
@@ -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:
diff --git a/bas.c b/bas.c
index 64c4f3f..230032c 100644
--- a/bas.c
+++ b/bas.c
@@ -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);
diff --git a/bas.h b/bas.h
index 3a65567..8168aa1 100644
--- a/bas.h
+++ b/bas.h
@@ -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();