/*************************************************************************** * mkdfs.c * * Creates an Acorn DFS disc image for use with a BBC Micro or emulator. * Copyright (C) 2006 Thomas Horsten (thomas@horsten.com). * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License * as published by the Free Software Foundation; version 2 only. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, * MA 02110-1301, USA. * * The DFS format is quite simple. I found the specifcation here: * http://www.nvg.ntnu.no/bbc/doc/WatfordDFS.txt * * Please note that the Watford extension with 62 files on a disc is * not supported by this utility. * * Version history: * * 1.00 - 2006/02/03 First version * ***************************************************************************/ #include #include #include #define VERSION "1.00" int gargc; char **gargv; struct dfsdirent { char *srcpath; char dir; char name[8]; int loadaddr; int runaddr; int startsect; int length; }; struct dfsdirent dirs[2][31]; void printhelp() { printf( "MKDFS version " VERSION " by Thomas Horsten \n" "Usage: mkdfs [OPTION|FILE]...\n" "Create an Acorn DFS disk image (single sided or interleaved double-sided).\n" "\n" "Options that affect the overall operation of the program:\n" " -c Displays copyright and about notice\n" " -h Displays this help text\n" " -o FNAME Specifies output filename - default: image.ssd/image.dsd\n" " -t NUMBER Select number of tracks (35/40/80) - default 80\n" " -x 0|1 Expand image to physical disc size (ie. 200k/400k) - default 1\n" "\n" "Options that affect the files added after that option (these can be repeated\n" "as many times as necessary):\n" " -b NUMBER Boot option (can be specified once per side) - default 0\n" " -d LETTER Specifies directory (default '$')\n" " -l NUMBER Specifies load address (default 0x1900)\n" " -L 0|1 1 means files are \"locked\" (write protected) - default 0\n" " -r NUMBER Specifies run address (default 0x1900)\n" " -s Switches to the second side of the disk (implies 2-sided image)\n" " -T STRING Specifies disk title (can be specified once per side)\n" " Default: MKDFSImage1 / MKDFSImage2\n" "\n" "Numbers may be prefixed with \"0x\" if they are in hex, e.g. \"0x1900\"\n" "Filenames will be translated to DFS format when possible. To manually specify\n" "the DFS filename, use \"sourcefilename|dfsfilename\" - e.g. \"Readme.txt|README\".\n" "\n" "Example: mkdfs -o Disk1.ssd -T MyDisk \"file1.bas|MYPRG\" file2 -s -T Side2 file3\n"); } void printcopyright() { printf( "MKDFS version " VERSION " by Thomas Horsten \n" "Copyright (C) 2006 Thomas Horsten (thomas@horsten.com).\n" "\n" "This program is free software; you can redistribute it and/or\n" "modify it under the terms of the GNU General Public License\n" "as published by the Free Software Foundation; version 2 only.\n" "\n" "This program is distributed in the hope that it will be useful,\n" "but WITHOUT ANY WARRANTY; without even the implied warranty of\n" "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n" "GNU General Public License for more details.\n" "\n" "You should have received a copy of the GNU General Public License\n" "along with this program; if not, write to the Free Software\n" "Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,\n" "MA 02110-1301, USA.\n" "\n" "The DFS format is quite simple. I found the specifcation here:\n" "http://www.nvg.ntnu.no/bbc/doc/WatfordDFS.txt\n" "\n" "Please note that the Watford extension with 62 files on a disc is\n" "not supported by this utility.\n" "\n" "See http://www.infowares.com/bbc for updates etc.\n"); } void seekto(FILE *f, int sides, int side, int sector) { int track = sector/10; int relsec = sector%10; int abssec = track * 10 * (sides + 1) + side * 10 + relsec; if (fseek(f, abssec*256, SEEK_SET)) { fprintf(stderr, "\nError seeking in image file: %s.\n", strerror(errno)); exit(1); } } int getstr(int *arg, char **val) { char *p; if (gargv[*arg][2]) { p = gargv[*arg]+2; } else if ((*arg+1)3)) { fprintf(stderr, "Boot option must be in the range 0-3.\n"); exit(1); } break; case 'L': if (getint(&arg,&locked)<0) { exit(1); } break; case 't': if (getint(&arg,&tracks)<0) { exit(1); } if (tracks != 35 && tracks != 40 && tracks != 80) { fprintf(stderr, "Number of tracks must be 35, 40 or 80.\n"); exit(1); } break; case 'x': if (getint(&arg,&expand)<0) { exit(1); } if (expand != 0 && expand != 1) { fprintf(stderr, "Expand setting must be 0 or 1.\n"); exit(1); } break; case 'd': if (getstr(&arg,&p)<0) { exit(1); } if (strlen(p) != 1) { fprintf(stderr, "Directory specifier must be a single character.\n"); exit(1); } dir=p[0]; break; case 'T': if (getstr(&arg,&title[side])<0) { exit(1); } if (strlen(title[side])>12) { title[side][12]='\0'; fprintf(stderr, "Maximum title length is 12. Truncated to \"%s\".\n", title[side]); } break; case 'o': if (getstr(&arg,&imgfile)<0) { exit(1); } break; case 's': if (side > 0) { fprintf(stderr, "Already writing side 2 - -s option invalid\n"); exit(1); } side=1; break; default: fprintf(stderr, "Invalid option - use -h for help\n"); exit(1); } } else { if (fcount[side]>30) { fprintf(stderr, "Too many files for DFS\n"); exit(1); } memset(dirs[side][fcount[side]].name, ' ', 7); char *p; p = strchr(argv[arg],'|'); if (p) { *p=0; strncpy(dirs[side][fcount[side]].name, p+1, 7); } else { p = strrchr(argv[arg], '/'); #ifdef WIN32 char *q; q = strrchr(argv[arg], '\\'); if (q > p) p = q; #endif if (p) { strncpy(dirs[side][fcount[side]].name, p+1, 7); } else { strncpy(dirs[side][fcount[side]].name, argv[arg], 7); } } int c; dirs[side][fcount[side]].name[7] = '\0'; for (c=0; c<7; c++) { if (dirs[side][fcount[side]].name[c] == '\0') dirs[side][fcount[side]].name[c] = ' '; } dirs[side][fcount[side]].srcpath = argv[arg]; dirs[side][fcount[side]].dir = dir | (locked ? 0x80 : 0); dirs[side][fcount[side]].loadaddr = loadaddr; dirs[side][fcount[side]].runaddr = runaddr; fcount[side]++; } } if (!imgfile) { imgfile = side>0 ? "image.dsd" : "image.ssd"; } fprintf(stderr, "Creating %s sided DFS image \"%s\"\n", side>0?"double":"single",imgfile); FILE *f = fopen(imgfile, "wb"); if (!f) { fprintf(stderr, "Error opening output file: %s.\n", strerror(errno)); } int cursect; char sectbuf[256]; for (c=0; c<=side; c++) { cursect = 2; fprintf(stderr, "Side %d: \"%s\", boot option %d (%s)\n", c, title[c], bootopt[c], bootoptnames[bootopt[c]]); for (d=0; d= tracks*10) { fprintf(stderr, "\nDisk image full while writing %s.\n", dirs[c][d].srcpath); exit(1); } if (side) { seekto(f,side,c,cursect); } if (fwrite(sectbuf, 1, 256, f) < 256) { fprintf(stderr, "\nError writing to image file: %s.\n", strerror(errno)); exit(1); } cursect++; dirs[c][d].length += r; } while (r > 0); fprintf(stderr, " %06X %03X\n", dirs[c][d].length, dirs[c][d].startsect); } // Now write directory sectors memset(sectbuf, 0, 256); strncpy(sectbuf, title[c], 8); int d; for (d=0; d 8) { strncpy(sectbuf, title[c]+8, 4); } sectbuf[5] = fcount[c]*8; sectbuf[6] = (bootopt[c] << 4) | (((tracks*10) >> 8) & 0x3); sectbuf[7] = (tracks*10) & 0xff; for (d=0; d> 8) & 0xff; sectbuf[8+pos*8 + 2] = dirs[c][d].runaddr & 0xff; sectbuf[8+pos*8 + 3] = (dirs[c][d].runaddr >> 8) & 0xff; sectbuf[8+pos*8 + 4] = dirs[c][d].length & 0xff; sectbuf[8+pos*8 + 5] = (dirs[c][d].length >> 8) & 0xff; sectbuf[8+pos*8 + 6] = (dirs[c][d].startsect >> 8) & 0x3 | ((dirs[c][d].loadaddr >> 16) & 0x3) << 2 | ((dirs[c][d].length >> 16) & 0x3) << 4 | ((dirs[c][d].runaddr >> 16) & 0x3) << 6; sectbuf[8+pos*8 + 7] = dirs[c][d].startsect & 0xff; } if (side) seekto(f,side,c,1); if (fwrite(sectbuf, 1, 256, f) < 256) { fprintf(stderr, "\nError writing directory part 2: %s.\n", strerror(errno)); exit(1); } } if ( expand && (cursect <= tracks*10*256) ) { seekto(f,side,side,tracks*10-1); memset(sectbuf, 0, 256); if (256 > fwrite(sectbuf, 1, 256, f)) { fprintf(stderr, "\nError writing final sector in image file: %s.\n", strerror(errno)); exit(1); } } fclose(f); }