#include "unalf.h" #include "addrs.h" int bad_checksum, bad_checksum_count = 0; int new_file = 0; unsigned int bytes_written = 0; char *out_filename; void dpoke(int addr, u16 value) { mem[addr] = value & 0xff; mem[addr + 1] = value >> 8; } u16 dpeek(int addr) { return mem[addr] | (mem[addr + 1] << 8); } static void bad_atari_filename(const char *msg) { char fn[50] = ""; char xbuf[5]; int i; u8 c; for(i = 0; (c = (u8)out_filename[i]) && i < 12; i++) { if(c < ' ' || c > '|') { /* not printable, insert a hex escape */ sprintf(xbuf, "$%02X", c); strcat(fn, xbuf); } else { strncat(fn, (char *)&c, 1); } } fprintf(stderr, "%s: bad Atari filename \"%s\": %s\n", self, fn, msg); } /* note to self: it's tempting to use isalpha(), isprint(), etc from ctype.h... but those are locale-aware. we want ASCII-only versions. */ static void sanity_check_filename(void) { u8 c; unsigned int i, bad = 0, dots = 0, uscore = 0; c = out_filename[0]; if(!c) { bad_atari_filename("empty! corrupt ALF file?"); return; } else if(c < 'A' || c > 'Z') { bad_atari_filename("does not begin with A-Z"); } for(i = 0; (c = out_filename[i]) && i < 12; i++) { if(c >= 'A' && c <= 'Z') continue; if(c >= '0' && c <= '9') continue; if(c == '_') { uscore++; continue; } if(c == '.') { dots++; continue; } bad++; } if(!dots) { bad_atari_filename("no \".\""); } else if(dots > 1) { bad_atari_filename("more than one \".\""); } if(bad) bad_atari_filename("invalid characters. corrupt ALF file?"); else if(uscore) fprintf(stderr, "%s: filename has underscore, OK on Sparta/MyDOS, not Atari DOS 2.x\n", self); } void fix_filename(void) { char *p; if(!out_filename) return; if(strlen(out_filename) > 12) { fprintf(stderr, "%s: filename in ALF header not null-terminated, fixing.\n", self); out_filename[12] = '\0'; } sanity_check_filename(); if(opts.lowercase) { for(p = out_filename; *p; p++) *p = tolower(*p); } if(!opts.keepdot) { for(p = out_filename; *p; p++) if(p[0] == '.' && !p[1]) *p = '\0'; } } void make_backup(void) { /* up to 12-char FILENAME.EXT, plus a ~, plus null terminator = 14 */ char backup[14]; strncpy(backup, out_filename, 13); strncat(backup, "~", 13); /* silently ignore errors! */ rename(out_filename, backup); } void extract_alf(void) { /* get ready to call fake 6502 stuff. set up memory like the Atari. */ dpoke(MEMTOP, 0xbc1f); while(read_alf_header()) { out_filename = (char *)(mem + alf_hdr_filename); fix_filename(); if(!file_wanted(out_filename)) { if(fseek(in_file, getquad(alf_hdr_compsize0), SEEK_CUR) != 0) { fprintf(stderr, "%s: fatal: seek failed on input!\n", self); exit(1); } out_filename = 0; continue; } if(!opts.quiet) { printf("%s %s\n", opts.testonly ? "Testing" : "Uncrunching", out_filename); } if(opts.extract_to_stdout) { out_file = stdout; } else { char *realname = out_filename; if(opts.testonly) { out_filename = "/dev/null"; } else if(!opts.overwrite) { make_backup(); } if(!(out_file = fopen(out_filename, "wb"))) { fprintf(stderr, "%s: fatal: ", self); perror(out_filename); exit(1); } out_filename = realname; } bad_checksum = bytes_written = 0; new_file = 1; uncrunch_file(); if(bad_checksum) bad_checksum_count++; if(bytes_written != getquad(alf_hdr_origsize0)) fprintf(stderr, "%s: %s should be %u bytes, but extracted to %u.\n", self, out_filename, getquad(alf_hdr_origsize0), bytes_written); if(!opts.extract_to_stdout) fclose(out_file); out_filename = 0; } if(opts.testonly && !opts.quiet) { if(bad_checksum_count) printf("%d file%s with bad checksum!\n", bad_checksum_count, bad_checksum_count == 1 ? "" : "s"); else printf("All files OK.\n"); } } void chksum_err(void) { bad_checksum = 1; fprintf(stderr, "%s: checksum error on %s\n", self, out_filename); } void unknown_err(void){ fprintf(stderr, "%s: fatal: unknown error (L7712)\n", self); exit(1); } void stack_underrun(void){ fprintf(stderr, "%s: fatal: stack underrun\n", self); exit(1); } void stack_overrun(void){ fprintf(stderr, "%s: fatal: stack overrun\n", self); exit(1); }