/* h8dinfo - This program opens a file in "H8D" format and ** performs various analyses of the disk image contents. ** It provides a menu of commands that let you: ** ** - List key information about the disk image ** - Dump key sectors such as RGT and GRT ** - Dump any specific sector (by number) ** - Print a directory listing ** - Dump the contents of a file (in hex) ** - Extract a file to HDOS ** ** Usage: h8dinfo ** ** Version 1.0a ** ** Compiled with Software Toolworks C/80 V. 3.0 ** ** Glenn Roberts 10 November 2013 ** v1.0a 2 September 2016 ** ** v1.0a Added code to support up to 800K disk sizes ** added "C3 1D 24 20" as a valid signature ** added support for cluster factors (spg) > 2 ** increased MAXD to 400 ** ** Bugs: will fail if cluster factor (sectors/group) > 8, ** however not aware of any floppy disk formats that ** would have such large cluster factors. ** */ #define TRUE 1 #define FALSE 0 #define SECSIZE 256 #define MAXD 400 /* maximum number of dir entries */ #define MAXCF 8 /* maximum supported Cluster Factor (spg) */ #define ESIZE 23 /* HDOS directory entry size */ #define DIFSYS 0200 /* System File */ #define DIFLOC 0100 /* Locked File */ #define DIFWP 040 /* Write Protected */ #define DIFCNT 020 /* Contiguous File */ /* Disk info structure - contained on sector 9 (from LABDEF.ACM) */ struct dkinfo { char volser; /* Serial number */ int idate; /* Date INITed */ int dirsector; /* Start of DIRectory */ int grtsector; /* GRT sector */ char spg; /* sectors per group */ char voltype; /* volume type */ char initver; /* INIT version used */ int rgtsector; /* RGT sector */ int volsize; /* volume size */ int sectsize; /* physical sector size */ char dkflags; /* flags */ char label[60]; /* disk label */ int reserved; /* reserved */ char spt; /* sectors per track */ char unused[176]; /* filler */ }dkbuff; /* HDOS file directory entry data structure */ struct finfo { char name[8]; /* file name */ char ext[3]; /* file extension */ char prj; /* project (not used) */ char ver; /* version (not used) */ char clf; /* cluster factor */ char flags; /* flags */ char rsvd; /* reserved */ char fgn; /* first group number */ char lgn; /* last group number */ char lsi; /* last sector index */ int credate; /* creation date */ int moddate; /* modification date */ int size; /* file size (sectors) */ char dname[13]; /* name in string form */ } fentry; /* array of pointers to directory entries */ struct finfo *direntries[MAXD]; char rwbuff[SECSIZE*MAXCF]; /* disk I/O buffer */ char grtbuff[SECSIZE]; /* GRT buffer */ char strbuf[32]; /* Console I/O buffer */ int channel; /* file I/O channel */ int nentries; #include "printf.c" #include "seek.c" /* hinfo - read key disk info off sector 09. Returns ** 1 if error encountered or 0 if no error */ int hinfo() { int i, rc; char *s, *d; rc = 0; /* Test if proper H8D format by examining the first few ** bytes of sector 0 for a proper signature (several varieties). */ readsectors(rwbuff,0,1); if ( ( rwbuff[0]==0xFFC3 && rwbuff[1]==0xFFA0 && rwbuff[2]==0x0022 && rwbuff[3]==0x0020 ) || ( rwbuff[0]==0xFFC3 && rwbuff[1]==0x001D && rwbuff[2]==0x0024 && rwbuff[3]==0x0020 ) || ( rwbuff[0]==0xFFAF && rwbuff[1]==0xFFD3 && rwbuff[2]==0x007D && rwbuff[3]==0xFFCD ) ) { /* have valid file - examine sector 09 for key info */ readsectors(rwbuff,9,1); /* copy buffer into data structure */ s = rwbuff; d = (char *) &dkbuff; /* copy into dkinfo structure */ for (i=0; ifgn & 0xFF; cc = 0; do { ++cc; clu = grtbuff[clu] & 0xFF; } while (clu != 0); /* convert file size to blocks */ direntries[nentries]->size = (cc-1)*dkbuff.spg + direntries[nentries]->lsi; /* fill out full file name */ sdir = direntries[nentries]->name; ddir = direntries[nentries]->dname; for (j=0; (j<8) && (*sdir!=0); j++) *ddir++ = *sdir++; sdir = direntries[nentries]->ext; if (*sdir != 0) { *ddir++ = '.'; for (j=0; (j<3) && (*sdir!=0); j++) *ddir++ = *sdir++; } *ddir = 0; nentries++; } } else { /* blank entry, skip over */ src += ESIZE; } } /* now find the link to the next chunk of directory */ if (!done) { /* skip 4 bytes */ src+=4; inxt = (int *) src; nxtsector = *inxt; if (nxtsector==0) done = TRUE; } } while (!done); printf("Done! %d files found\n", nentries); } /* listdir - print directory listing from ** stored array. */ listdir() { int i, j, entries, siztot; char *c; entries = 0; siztot = 0; /* list directory */ for (i=0; iname; if (isprint(*c)) { ++entries; /* print name and extension */ for (j=0; j<8; j++, c++) putchar((*c==0) ? ' ' : *c); putchar('.'); c = direntries[i]->ext; for (j=0; j<3; j++, c++) putchar((*c==0) ? ' ' : *c); /* print flags */ printf(" %c%c%c%c ", ((direntries[i]->flags & DIFSYS) ? 'S' : ' '), ((direntries[i]->flags & DIFLOC) ? 'L' : ' '), ((direntries[i]->flags & DIFWP) ? 'W' : ' '), ((direntries[i]->flags & DIFCNT) ? 'C' : ' ')); /* print file size in blocks */ siztot += direntries[i]->size; printf(" %4d ", direntries[i]->size); /* print mod date */ prdate(direntries[i]->moddate); /* print full file name */ printf(" %s", direntries[i]->dname); printf("\n"); } } printf("Files %d, Total %d\n", entries, siztot); } /* dumpfile - dump the contents of a file in hex */ dumpfile(fname) char *fname; { int ifile, i, sc; unsigned clu; if ((ifile=match(fname)) == -1) printf("File %s not found!\n", fname); else { printf("Dumping file %s\n", direntries[ifile]->dname); /* trace cluster chain and dump the file ** one sector at a time */ clu = direntries[ifile]->fgn & 0xFF; sc = direntries[ifile]->size; while (sc > 0) { /* read a cluster's worth of sectors */ readsectors(rwbuff,clu*dkbuff.spg,dkbuff.spg); /* dump the cluster one sector at a time */ for (i=0; (i0); i++, sc--) hexdump(rwbuff+(i*SECSIZE),SECSIZE); /* point to next cluster in the chain */ clu = grtbuff[clu] & 0xFF; } } } /* extract - extract a file and save it to HDOS */ extract(fname) char *fname; { int ifile, i, sc, chan; unsigned clu; if ((ifile=match(fname)) == -1) printf("File %s not found!\n", fname); else if ((chan = fopen(fname, "wb")) == 0) printf("Error opening destination file %s\n", fname); else { printf("Extracting file %s\n", direntries[ifile]->dname); /* trace cluster chain and dump the file ** one sector at a time */ clu = direntries[ifile]->fgn & 0xFF; sc = direntries[ifile]->size; while (sc > 0) { /* read a cluster's worth of sectors */ readsectors(rwbuff,clu*dkbuff.spg,dkbuff.spg); /* write the data one sector at a time */ for (i=0; (i0); i++, sc--) { write(chan, rwbuff+(i*SECSIZE),SECSIZE); /* show progress... */ putchar('.'); } /* point to next cluster in the chain */ clu = grtbuff[clu] & 0xFF; } printf("%d sectors written\n", direntries[ifile]->size); fclose(chan); } } /* match - match file name to directory entry. ** Returns the index of the file name in the ** directory array, or -1 if not found */ match(fname) char *fname; { int i, rc, match; rc = -1; printf("Looking for match to: %s\n", fname); /* loop over directory entries looking for a match */ for (i=0, match=FALSE; (idname) == 0) { match = TRUE; rc = i; } return rc; } /* printinfo - print information on H8D file */ printinfo() { int i; char *c; printf("Volume = %d\n", dkbuff.volser); printf("Label: '"); for (i=0, c=dkbuff.label; i<60; i++) putchar(*c++); printf("'\n"); printf("INIT date = "); prdate(dkbuff.idate); printf("\n"); printf("Start of DIRectory = %d\n", dkbuff.dirsector); printf("GRT sector = %d\n", dkbuff.grtsector); printf("Sectors/group = %d\n", dkbuff.spg); printf("Vol Type = %d\n", dkbuff.voltype); printf("INIT version = %d\n", dkbuff.initver); printf("RGT sector = %d\n", dkbuff.rgtsector); printf("Volume size = %d\n", dkbuff.volsize); printf("Sector size = %d\n", dkbuff.sectsize); printf("Flags = %d\n", dkbuff.dkflags); printf("Sectors/track = %d\n", dkbuff.spt); printf("\n"); } /* prdate - print date stored in HDOS format */ prdate(d) int d; { int day, mo, yr; static char *month[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; if (d == 0) printf(" No-Date "); else { /* day in rightmost 5 bits */ day = d & 0x1F; /* next 4 bits are month */ mo = (d / 32) & 0x0F; /* next 6 bits are yr - 1970 */ yr = 1970 + ((d / 512) & 0x3F); printf("%02d-%s-%02d", day, month[mo-1], yr-1900); } } /* hexdump - dump a buffer to stdout in hexadecimal format */ hexdump(b,n) char *b; int n; { int i; /* print header */ printf("\n\n\t0 1 2 3 4 5 6 7 8 9 A B C D E F"); printf(" 0123456789ABCDEF\n"); printf("\t\b-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --"); printf(" ----------------\n"); for (i=0; i0x1F) && (c<0x7F)); } strupper(s) char *s; { char *c; for (c=s; *c; c++) *c = toupper(*c); } main(argc, argv) int argc; char *argv[]; { int done, secno; if (argc < 2) printf("Usage: %s \n", argv[0]); else if((channel = fopen(argv[1], "rb")) == 0) printf("Unable to open source file %s\n", argv[1]); /* attempt to read critical disk parameters off sector 9 */ else if(hinfo(channel)) printf("File %s not a valid H8D file!\n", argv[1]); else { /* valid file - first load the GRT */ readsectors(grtbuff,dkbuff.grtsector,1); /* build and populate directory array */ builddir(); printf("H8D Sector Dump and Analysis Utility\n\n"); done == FALSE; while (!done) { printf("Command: I)nfo H)elp F)ile D)ir G)RT R)GT S)ector eX)tract Q)uit\n"); getline(strbuf,32); switch(toupper(strbuf[0])) { case 'I': printinfo(); break; case 'H': printf("Commands:\n\n"); printf("\tInfo:\tDisplay key info on H8D file\n"); printf("\tHelp:\tDisplay help\n"); printf("\tFile:\tDisplay contents of a file\n"); printf("\tDir:\tList a file directory of H8D contents)\n"); printf("\tGRT:\tDump the Group Reservation Table (GRT)\n"); printf("\tRGT:\tDump the Reserved Group Table (RGT)\n"); printf("\tSector:\tDump a specified sector\n"); printf("\teXtract:\tExtract a file to HDOS\n"); printf("\tQuit: \tExit\n\n"); break; case 'D': listdir(); break; case 'F': printf("File to display? : "); getline(strbuf,32); strupper(strbuf); dumpfile(strbuf); break; case 'X': printf("File to extract? : "); getline(strbuf,32); strupper(strbuf); extract(strbuf); break; case 'S': printf("Which Sector ? : "); getline(strbuf,32); secno = atoi(strbuf); if (readsectors(rwbuff,secno,1) != -1) hexdump(rwbuff,SECSIZE); else printf("Error attempting to read sector %d\n", secno); break; case 'R': printf("Dumping RGT from sector %d\n", dkbuff.rgtsector); readsectors(rwbuff,dkbuff.rgtsector,1); hexdump(rwbuff,SECSIZE); break; case 'G': printf("Dumping GRT from sector %d\n", dkbuff.grtsector); hexdump(grtbuff,SECSIZE); break; case 'Q': printf("Exiting...\n"); done = TRUE; break; } } fclose(channel); } } #include "stdlib.c"