#include "axe.h" extern int all, total_sec, translate; static int atr_offset; int load_disk(unsigned char *diskbuf, char *filename) { FILE *file; int vtoc_sec; unsigned char tmpbuf[16]; char tmp[128]; if((file = fopen(filename, "r")) == NULL) { sprintf(tmp, "load_disk(): %s", filename); perror(tmp); exit(1); } fread(tmpbuf, 16, 1, file); atr_offset = 16; memcpy(diskbuf, tmpbuf, 16); if((tmpbuf[0] != 0x96) || (tmpbuf[1] != 0x02)) { /* not an atr */ atr_offset = 0; rewind(file); total_sec = 720; } /* do some sanity checking of images: */ if(atr_offset) { total_sec = (diskbuf[2] + 256 * diskbuf[3]) >> 3; if(!total_sec) total_sec = (diskbuf[6] + 256 * diskbuf[7]) * 8192; if(total_sec < 720) { printf("Total sectors (%d) < 720 - aborting\n", total_sec); exit(1); } if(total_sec > 720) { printf ("Warning: Total sectors (%d) > 720 - only reading first 720 sectors\n", total_sec); } if((diskbuf[4] + 256 * diskbuf[5]) != 128) { printf("Fatal: sector size not 128 bytes!\n"); exit(1); } } else { printf ("Warning: blindly assuming xfd image is 720 sectors, 128 bytes/sector!\n"); } if((total_sec = fread(diskbuf + atr_offset, 128, 720, file)) < 720) { printf("Error reading image file, only got %d sectors, aborting\n", total_sec); perror("fread"); exit(1); } vtoc_sec = diskbuf[atr_offset + 128 * 359 + 1] + 256 * diskbuf[atr_offset + 128 * 359 + 2]; if(vtoc_sec != 707) printf ("Warning: VTOC sector count (%d) not 707, are you sure this is an AtariDOS 2.0 image?\n", vtoc_sec); return 0; } int write_disk(unsigned char *diskbuf, char *filename) { FILE *file; unsigned char tmpbuf[16]; file = fopen(filename, "w"); memcpy(tmpbuf, diskbuf, 16); atr_offset = 0; if((tmpbuf[0] == 0x96) && (tmpbuf[1] == 0x02)) { /* is an atr */ fwrite(tmpbuf, 16, 1, file); atr_offset = 16; } fwrite(diskbuf + atr_offset, 128, 720, file); fclose(file); return 0; } void usage(char *name) { printf ("Usage: %s [options] [-D filename] [-w filename] [-e filename]\n\t\t [-b filename] [-c dirname] [-d sector] disk_image\n\n", name); printf("\t-a\tlist all dir entries, even deleted/empty ones\n"); printf("\t-l\ttrace & print links for all files on disk\n"); printf("\t-v\tdump VTOC (sector 360) in decimal, hex, and binary\n"); printf ("\t-t\t'tar xf' style - create dir filled with files in image\n"); printf("\t-u\tUn*x <-> Atari newline/EOL translation\n"); printf("\t-D\tdelete file from image\n"); printf("\t-w\twrite file to image\n"); printf("\t-c\tcreate new image from directory\n"); printf("\t-x\textract (read) file from image, write to current dir\n"); printf("\t-b\tcreate blank image called 'filename'\n"); printf("\t-d\tdump a sector in decimal, hex, and binary\n"); printf ("\ndisk_image must be a 720 sector, single density .atr or .xfd image\n"); printf("\tin Atari DOS 2.0 or compatible format\n"); printf ("\nAll UN*X filenames must conform to Atari 8.3 filename.ext format\n"); printf("\t(though they are NOT treated case-sensitively)\n"); printf("\nEnjoy!\n\n"); exit(1); } void print_entry(unsigned char *buf) { #ifdef DEBUG static int ents; int i; #endif int s; unsigned char tmpbuf[32]; memcpy(tmpbuf, buf, 16); tmpbuf[16] = '\0'; #ifdef DEBUG printf("successfully read dir. entry #%d\n", ents++); #endif if((tmpbuf[0] & 64) || all) { #ifdef DEBUG for (i = 0; i < 16; i++) { printf(" 0x%X", tmpbuf[i]); } #endif total_sec += (s = tmpbuf[1] + 256 * tmpbuf[2]); printf("%c %s\t%03d\n", (tmpbuf[0] & 32) ? '*' : ' ', tmpbuf + 5, s); } } void read_sector(unsigned char *disk, int sector, unsigned char *buf) { memcpy(buf, disk + (sector - 1) * 128 + atr_offset, 128); } void write_sector(unsigned char *disk, int sector, unsigned char *buf) { memcpy(disk + (sector - 1) * 128 + atr_offset, buf, 128); } void traverse_file(unsigned char *disk, unsigned char *ent, int action) { int i, next_sec, data_bytes, curfile; unsigned char buf[128]; unsigned char vtoc[128]; #ifdef DEBUG printf("traverse_file called with %s\n", (action - 1) ? "TF_DELETE" : "TF_PRINT"); #endif if(action == TF_DELETE) read_sector(disk, 360, vtoc); next_sec = ent[3] + 256 * ent[4]; while(next_sec) { if(action == TF_DELETE) mark_free(vtoc, next_sec); if(action == TF_PRINT) printf("%d: ", next_sec); read_sector(disk, next_sec, buf); data_bytes = buf[127]; curfile = buf[125] >> 2; next_sec = buf[126] + 256 * (buf[125] & 3); if(action == TF_PRINT) printf("data_bytes=%d filenum=%d next_sec=%d\n", data_bytes, curfile, next_sec); } if(action == TF_DELETE) { i = (vtoc[3] + 256 * vtoc[4]); #ifdef DEBUG printf("deleting file, old vtoc free count=%d", i); #endif i += ent[1] + 256 * ent[2]; #ifdef DEBUG printf(", new=%d\n", i); #endif vtoc[3] = i % 256; vtoc[4] = i / 256; write_sector(disk, 360, vtoc); if(action == TF_DELETE) { printf("Deleted file, now %d free sectors on image\n", i); } } } void dump_file(unsigned char *disk, unsigned char *ent, char *outfile) { #ifdef DEBUG int curfile; #endif int i, next_sec, data_bytes, total_written = 0; unsigned char buf[128]; FILE *out; /* fseek(handle,atr_seek(361)+16*filenum,SEEK_SET); fread(buf,16,1,handle); */ next_sec = ent[3] + 256 * ent[4]; out = fopen(outfile, "wb"); /* #ifdef DEBUG printf("First sector of filenum %d, ",filenum); #endif */ while(next_sec) { #ifdef DEBUG printf("%d: ", next_sec); #endif read_sector(disk, next_sec, buf); data_bytes = buf[127]; next_sec = buf[126] + 256 * (buf[125] & 3); #ifdef DEBUG curfile = buf[125] >> 2; printf("data_bytes=%d filenum=%d next_sec=%d\n", data_bytes, curfile, next_sec); #endif if(translate) for (i = 0; i <= data_bytes; i++) if(buf[i] == 0x9b) buf[i] = '\n'; fwrite(buf, data_bytes, 1, out); total_written += data_bytes; fflush(out); } printf("Wrote %d bytes to file %s\n", total_written, outfile); } int parse_filename(char *filename, char *result) { int i = 0, j = 0; char ext[4] = " \0"; char fn[9] = " \0"; #ifdef DEBUG printf("parse_filename called, filename=%s\n", filename); #endif if(strlen(filename) > 12) { printf("filename too long: %s\n", filename); exit(1); } /* if(strlen(strstr(filename,"."))>4) { printf("extension too long: %s\n",filename); exit(1); } */ while((filename[i]) && (filename[i] != '.')) { fn[i++] = toupper(filename[i]); } if(filename[i] == '.') while(filename[++i]) { ext[j++] = toupper(filename[i]); } strcpy(result, fn); strncat(result, ext, 3); #ifdef DEBUG printf("parse_filename exiting, result=%s\n", result); #endif return (0); } int make_filename(char *filename, char *result) { int i = 0, j = 8; char fn[13] = { '\0' }; while((filename[i] && filename[i] != ' ') && (i < 8)) { fn[i++] = tolower(filename[i]); } if(filename[j] != ' ') { fn[i++] = '.'; while(filename[j]) { fn[i++] = tolower(filename[j++]); } } #ifdef DEBUG printf("made filename %s from %s\n", fn, filename); #endif strcpy(result, fn); return 0; } int make_dir(char *filename) { char tmp[15] = { '\0' }; char *p; strcpy(tmp, filename); if(!(p = strrchr(tmp, '.'))) { strcat(tmp, ".dir"); } else { *p = '\0'; } #ifdef DEBUG printf("made dir: %s\n", tmp); #endif if(mkdir(tmp, 0777)) { perror("Can't make directory: "); exit(1); } chdir(tmp); return 0; } void dump_sector(unsigned char *disk, int sector) { unsigned char buf[256]; int i, j, k; printf("Dump of sector %d in decimal:\n", sector); read_sector(disk, sector, buf); for (i = 0; i < 128; i += 16) { printf("%03d: ", i); for (j = 0; j < 16; j++) { printf("%03d ", buf[i + j]); } printf("\n"); } printf("Dump of sector %d in hex:\n", sector); for (i = 0; i < 128; i += 16) { printf("%03x: ", i); for (j = 0; j < 16; j++) { printf(" %02x ", buf[i + j]); } printf("\n"); } printf("Dump of sector %d in binary:\n", sector); for (i = 0; i < 128; i += 16) { printf("%03x: ", i); for (j = 0; j < 16; j++) { for (k = 7; k > -1; k--) { if(buf[i + j] & (1 << k)) printf("1"); else printf("0"); } printf(" "); if(j == 7) printf("\n "); } printf("\n"); } } /* please, ignore the ugliness of this function:*/ void write_blank_disk(char *filename) { unsigned char atr_header[16] = { 0x96, 0x02, 0x80, 0x16, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; unsigned char disk[128 * 720 + 16] = { '\0' }; int i; unsigned char vtoc[128] = { 0x02, 0xc3, 0x02, 0xc3, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0f }; for (i = 11; i < 100; i++) vtoc[i] = 0xff; vtoc[55] = 0x00; vtoc[56] = 0x7f; for (i = 100; i < 128; i++) vtoc[i] = 0x00; atr_offset = 16; memcpy(disk, atr_header, 16); write_sector(disk, 360, vtoc); write_disk(disk, filename); printf("Wrote formatted blank image to %s\n", filename); } static void xlate_eofs(unsigned char *buf, int len) { int i; for (i = 0; i < len; i++) if(buf[i] == '\n') buf[i] = 0x9b; } void write_file(unsigned char *disk, char *filename) { unsigned char vtoc[128]; unsigned char buf[128]; char fnbuf[15]; /* unsigned char dentry[16];*/ unsigned char *dindex; int i, dentryno, first_sec, secno, next_sec, count = 0, bytes; FILE *handle; printf("Writing %s to image...", filename); parse_filename(filename, fnbuf); if((handle = fopen(filename, "r")) == NULL) { perror("write_file::fopen"); exit(1); } #ifdef DEBUG printf("write_file: parsed %s as \"%s\"\n", filename, fnbuf); #endif if((dentryno = get_dentry(disk)) == -1) { printf("write_file: no free directory entries on image\n"); exit(1); } #ifdef DEBUG printf("dentryno=%d\n", dentryno); #endif read_sector(disk, 360, vtoc); if(!(first_sec = secno = get_free_sector(vtoc))) { printf("write_file: no free sectors in image\n"); exit(1); } #ifdef DEBUG printf("first_sec=%d\n", first_sec); #endif do { int tmp, eof = 0; count++; bytes = fread(buf, 1, 125, handle); if(bytes == 125) { /* test for EOF on handle */ tmp = fgetc(handle); if(tmp == EOF) eof = 1; else ungetc(tmp, handle); } if(translate) xlate_eofs(buf, 125); mark_used(vtoc, secno); if(bytes == 125 && !eof) { /* full sector, not at EOF */ next_sec = get_free_sector(vtoc); buf[125] = (dentryno << 2) | (next_sec >> 8); buf[126] = next_sec & 255; buf[127] = 125; write_sector(disk, secno, buf); secno = next_sec; } else { /* partial sector or full sector at EOF */ buf[125] = dentryno << 2; buf[126] = 0; buf[127] = bytes; write_sector(disk, secno, buf); next_sec = 0; } } while(next_sec); /* update dentry */ read_sector(disk, 361 + (dentryno / 8), buf); dindex = buf + (dentryno % 8) * 16; dindex[0] = 66; /* taken from a dos 2.0s disk, hope its right */ dindex[1] = count & 255; dindex[2] = count / 256; dindex[3] = first_sec & 255; dindex[4] = first_sec / 256; memcpy(dindex + 5, fnbuf, 11); write_sector(disk, 361 + (dentryno / 8), buf); /*all thats left to do is update free sector count in vtoc: */ i = (vtoc[3] + 256 * vtoc[4]) - count; vtoc[3] = i & 255; vtoc[4] = i / 256; write_sector(disk, 360, vtoc); printf("Wrote %d sectors.\n", count); } int get_dentry(unsigned char *disk) { int i = 361, j, dentryno = 0, done = 0; unsigned char buf[128]; while(!done) { read_sector(disk, i, buf); for (j = 0; j < 128; j += 16) { if((buf[j] == 0) || (buf[j] & 128)) { return dentryno; } dentryno++; } done = (++i == 369); } return -1; } int get_free_sector(unsigned char *vtoc) { int i; for (i = 1; i < 720; i++) { /* atari DOS doesn't use sector 720 */ if(vtoc[vtoc_byte(i)] & vtoc_bit(i)) return i; } return 0; } int vtoc_byte(int sector) { return 10 + (sector) / 8; } int vtoc_bit(int sector) { return 128 >> ((sector) % 8); } void mark_used(unsigned char *vtoc_sector, int sector) { vtoc_sector[vtoc_byte(sector)] &= ~vtoc_bit(sector); } void mark_free(unsigned char *vtoc_sector, int sector) { vtoc_sector[vtoc_byte(sector)] |= vtoc_bit(sector); } /*int get_confirm(char *prompt) { printf("%s[y/N]? ",prompt); return ((toupper(getc(stdin)))=='Y'); }*/ int check_dir(unsigned char *disk, char *filename) { int i = 361, j, done = 0, dentryno = 0; unsigned char buf[128]; printf("check_dir: passed %s\n", filename); while(!done) { read_sector(disk, i, buf); for (j = 0; j < 128; j += 16) { if((strncmp(filename, (char*)(buf + j + 5), 11) == 0)) { return dentryno; } dentryno++; } done = (++i == 369); } return -1; } /* these routines suck, and are not actually used any more. * maybe someday they'll become useful again. * int delete_file(unsigned char *disk,char *filename) { int first_sec,next_sec,i,dentryno; char fnbuf[15]; char buf[128]; char *debuf; char vtoc[128]; printf("delete_file doesn't work yet. passed: %s\n",filename); parse_filename(filename,fnbuf); if((dentryno=check_dir(disk,fnbuf))==-1) { printf("delete_file: file %s not found on image\n",fnbuf); exit(1); } printf("delete_file: would delete %s\n",fnbuf); read_sector(disk,361+dentryno/8,buf); read_sector(disk,360,vtoc); debuf=buf+(dentryno%8)*16; next_sec=debuf[3]+256*debuf[4]; traverse_file(disk,debuf,TF_DELETE); return 0; } */