#include "unalf.h" #include "addrs.h" #include "sanity.h" #include #include 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); } 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(out_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 set_datetime() { u16 t, d; struct tm tm; time_t time; struct utimbuf utb; t = dpeek(alf_hdr_time0); /* don't set the date/time at all, if it's zero */ if(!t) { fprintf(stderr, "%s: warning: date/time for %s is zero, using current date/time\n", self, out_filename); return; } d = dpeek(alf_hdr_date0); tm.tm_isdst = -1; tm.tm_sec = (t & 0x1f) << 1; tm.tm_hour = t >> 11; if(tm.tm_hour == 24) tm.tm_hour = 0; tm.tm_min = (t >> 5) & 0x3f; tm.tm_year = (d >> 9) + 1980 - 1900; tm.tm_mon = ((d >> 5) & 0x0f) - 1; tm.tm_mday = (d & 0x1f); time = mktime(&tm); if(time != (time_t) -1) { utb.actime = utb.modtime = time; utime(out_filename, &utb); /* ignore errors */ } } void make_backup(const char *fname) { /* up to 12-char FILENAME.EXT, plus a ~, plus null terminator = 14 */ /* has to handle up to 16 chars for -s option: LONGFILE.NAME.ALF */ char backup[20]; strncpy(backup, fname, 19); strncat(backup, "~", 19); /* silently ignore errors! */ rename(fname, backup); } void open_out_file(const char *fname) { if(opts.testonly) { fname = "/dev/null"; } else if(!opts.overwrite) { make_backup(fname); } if(!(out_file = fopen(fname, "wb"))) { fprintf(stderr, "%s: fatal: ", self); perror(fname); exit(1); } } void truncated_err(void); void write_split_file(void) { int i, c; unsigned int len; char splitname[20]; strncpy(splitname, out_filename, 13); strncat(splitname, ".ALF", 6); if(!opts.quiet) printf("Writing %s\n", splitname); len = getquad(alf_hdr_compsize0); open_out_file(splitname); fwrite(&mem[alf_header], 1, 29, out_file); for(i = 0; i < len; i++) { c = fgetc(in_file); if(c == EOF) { truncated_err(); } fputc(c, out_file); } fclose(out_file); } void extract_alf(void) { int files_extracted = 0, skip, file_number = 0; /* get ready to call fake 6502 stuff. set up memory like the Atari. */ dpoke(MEMTOP, 0xbc1f); while(read_alf_header()) { file_number++; skip = 0; out_filename = (char *)(mem + alf_hdr_filename); fix_filename(); if(opts.extract_num) skip = (opts.extract_num != file_number); else skip = !file_wanted(out_filename); if(skip) { if(!opts.quiet) printf("Skipping %s\n", 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; } files_extracted++; if(opts.split) { write_split_file(); continue; } if(!opts.quiet) { printf("%s %s\n", opts.testonly ? "Testing" : "Uncrunching", out_filename); } if(opts.extract_to_stdout) { out_file = stdout; } else { open_out_file(out_filename); } 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); if(!opts.ignore_datetime) set_datetime(); } 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"); } if(!opts.quiet && !files_extracted) { printf("No files %s!\n", opts.testonly ? "tested" : "extracted"); } } void chksum_err(void) { bad_checksum = 1; fprintf(stderr, "%s: checksum error on %s\n", self, out_filename); } void truncated_err(void){ fprintf(stderr, "%s: fatal: compressed data is truncated, EOF before end marker\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); }