#include "unddi.h" #include "nls.h" int extr; int extdirs; int forceoffs; long imgoffs; int overwry = 0; int overwrn = 0; int uselfn; int locase; char *imgfile; FILE *f; int imgfmt; __u8 ddimap[160]; struct tbootsec bootsect; __u8 mediaid; unsigned short *fat; int fatentr; int shortened; int err; int errtot = 0; #ifndef __linux__ #define strcasecmp(s1,s2) stricmp(s1,s2) #endif int parsecmdline(int argc, char *argv[]) { int cmdl = 0; int cmdx = 0; int swo = 0; int tmpforceoffs = 0; unsigned long tmpoffs; int swnolfn = 0; int swnolocase = 0; int i; /* incorrect number of parameters? */ if (argc < 3) return -1; if (argc > 8) return -1; /* checking for commands */ if (strcmp(argv[1], "l") == 0) cmdl = 1; if (strcmp(argv[1], "x") == 0) { cmdx = 1; extdirs = 1; } if (strcmp(argv[1], "e") == 0) { cmdx = 1; extdirs = 0; } if (! (cmdl || cmdx)) return -1; /* checking for switches */ for (i=2; i\n", VER_MAJOR, VER_MINOR, __DATE__); printf("Latest sources and executables: http://www.ii.pw.edu.pl/~borkowsm/unddi.htm\n"); printf("This program is distributed under the terms of the GNU General Public License\n"); printf("\n"); printf("Usage:\n"); printf("List contents of disk image:\n"); printf(" unddi l [-o offset] imagefile\n"); printf("Extract files from disk image:\n"); #ifdef LFN_AVAIL printf(" unddi x [-o offset] [-oy | -on] [-nolfn] [-nolocase] imagefile\n"); #else printf(" unddi x [-o offset] [-oy | -on] imagefile\n"); #endif printf("Extract files from disk image without restoring directory structure:\n"); #ifdef LFN_AVAIL printf(" unddi e [-o offset] [-oy | -on] [-nolfn] [-nolocase] imagefile\n"); #else printf(" unddi e [-o offset] [-oy | -on] imagefile\n"); #endif printf("\n"); printf("-o offset : specify offset of disk image within imagefile\n"); printf(" offset can be decimal, hexadecimal (0xoffset) or octal (0offset)\n"); printf(" default is autodetection\n"); printf("-oy | -on : overwrite existing files / don't overwrite existing files\n"); printf(" default is prompt user\n"); #ifdef LFN_AVAIL printf("-nolfn : don't use long filenames\n"); printf("-nolocase : don't convert long filenames to lowercase\n"); printf(" short filenames are always converted to lowercase\n"); #endif #ifdef __linux__ printf("\n"); #endif } long findimg(void) { int imgfound = 0; long fileoffs = 0; long imgoffs; int actread; __u8 buf[4096]; __u8 *srchst; __u8 *magpos; char mag5[5]; __u16 mag2; f = fopen(imgfile, "rb"); if (!f) { printf("Unable to open %s: %s\a\n", imgfile, strerror(errno)); return ERR_OPEN; } while (1) { fseek(f, fileoffs, SEEK_SET); actread = fread(buf, 1, sizeof(buf), f); if (!actread) break; srchst = buf; while (1) { if (srchst-buf >= actread) break; magpos = memchr(srchst, 0xEB, actread-(srchst-buf)); if (!magpos) break; fseek(f, fileoffs + (magpos - buf) + 0x36, SEEK_SET); if (fread(&mag5, 1, sizeof(mag5), f) == sizeof(mag5)) { if (strncmp(mag5, FAT12_MAGIC, strlen(FAT12_MAGIC)) == 0) { imgfound = 1; break; } } fseek(f, fileoffs + (magpos - buf) + 510, SEEK_SET); if (fread(&mag2, 1, sizeof(mag2), f) == sizeof(mag2)) { if (mag2 == 0xAA55) { imgfound = 1; break; } } fseek(f, fileoffs + (magpos - buf) + 513, SEEK_SET); if (fread(&mag2, 1, sizeof(mag2), f) == sizeof(mag2)) { if (mag2 == 0xFFFF) { imgfound = 1; break; } } srchst = magpos + 1; } if (imgfound) { break; } fileoffs = fileoffs + sizeof(buf); } fclose(f); if (!imgfound) { printf("No FAT12 disk image found\a\n"); return ERR_BADFILE; } imgoffs = fileoffs + (magpos - buf); return imgoffs; } int chkfmt(void) { int actread; __u16 mag2; f = fopen(imgfile, "rb"); if (!f) { printf("Unable to open %s: %s\a\n", imgfile, strerror(errno)); return ERR_OPEN; } actread = fread(&mag2, 1, sizeof(mag2), f); if (actread != sizeof(mag2)) { printf("Invalid disk image\a\n"); return ERR_BADFILE; } fclose(f); if (mag2 == 0x4D49) { return FMT_DDI; } else { return FMT_RAW; } } int readddimap(void) { int i; int actread; f = fopen(imgfile, "rb"); if (!f) { printf("Unable to open %s: %s\a\n", imgfile, strerror(errno)); return ERR_OPEN; } for (i=0; i<160; i++) { fseek(f, 0x65 + 6*i, SEEK_SET); actread = fread(&ddimap[i], 1, sizeof(__u8), f); if (actread != sizeof(__u8)) { printf("Invalid disk image\a\n"); return ERR_BADFILE; } } fclose(f); return 0; } size_t imgread(FILE *stream, long offset, size_t size, void *ptr) { int tracksize; int i, tracklo, trackhi; int toffs, tsize; int remsize; size_t actread; if (imgfmt == FMT_DDI) { tracksize = bootsect.bytesec * bootsect.sectrack; tracklo = offset / tracksize; trackhi = (offset + size - 1) / tracksize; toffs = offset % tracksize; remsize = size; for (i=tracklo; i<=trackhi; i++) { if (remsize > tracksize-toffs) { tsize = tracksize-toffs; } else { tsize = remsize; } fseek(stream, imgoffs + (long)(ddimap[i]-1)*(long)tracksize + (long)toffs, SEEK_SET); actread = fread(ptr, 1, tsize, stream); if (actread != tsize) return size - remsize + actread; ptr = (__u8 *) ptr + tsize; toffs = 0; remsize -= tsize; } return size; } else { fseek(stream, imgoffs + offset, SEEK_SET); actread = fread(ptr, 1, size, stream); return actread; } } int readbootsec(void) { int actread; f = fopen(imgfile, "rb"); if (!f) { printf("Unable to open %s: %s\a\n", imgfile, strerror(errno)); return ERR_OPEN; } fseek(f, imgoffs, SEEK_SET); actread = fread(&bootsect, 1, sizeof(struct tbootsec), f); if (actread != sizeof(struct tbootsec)) { printf("Invalid disk image\a\n"); return ERR_BADFILE; } fclose(f); return 0; } int readmediaid(void) { int actread; f = fopen(imgfile, "rb"); if (!f) { printf("Unable to open %s: %s\a\n", imgfile, strerror(errno)); return ERR_OPEN; } fseek(f, imgoffs+512, SEEK_SET); actread = fread(&mediaid, 1, sizeof(mediaid), f); if (actread != sizeof(mediaid)) { printf("Invalid disk image\a\n"); return ERR_BADFILE; } fclose(f); return 0; } int comparefats(long fat1offs, long fat2offs, int fatsize) { void *fat1, *fat2; int actread; fat1 = malloc(fatsize); if (!fat1) { printf("Unable to allocate memory\a\n"); exit(1); } fat2 = malloc(fatsize); if (!fat2) { printf("Unable to allocate memory\a\n"); exit(1); } f = fopen(imgfile, "rb"); if (!f) { free(fat2); free(fat1); printf("Unable to open %s: %s\a\n", imgfile, strerror(errno)); return ERR_OPEN; } actread = imgread(f, fat1offs, fatsize, fat1); actread = imgread(f, fat2offs, fatsize, fat2); if (actread != fatsize) { fclose(f); free(fat2); free(fat1); printf("Invalid disk image\a\n"); return ERR_BADFILE; } fclose(f); if (memcmp(fat1, fat2, fatsize)) printf("Warning: FAT copies are different\a\n"); free(fat2); free(fat1); return 0; } int readfat(int entries) { int i; struct { unsigned clust1 : 12; unsigned clust2 : 12; } dblclust; f = fopen(imgfile, "rb"); if (!f) { printf("Unable to open %s: %s\a\n", imgfile, strerror(errno)); return ERR_OPEN; } for (i=0; i> 1)) + s[i]; } return cksum; } void dispfile(union tdirent *dir, char *lfn) { printf("%.8s", dir->s.name); if (strncmp(dir->s.ext, " ", 3) == 0) { printf(" "); } else { printf("."); } printf("%.3s ", dir->s.ext); if (dir->s.attr & ATTR_DIR) { printf(" "); } else { printf("%9ld", dir->s.size); } printf(" %4d-%02d-%02d %2d:%02d", dir->s.dt.year+1980, dir->s.dt.mon, dir->s.dt.mday, dir->s.dt.hour, dir->s.dt.min); if (strlen(lfn)) { printf(" %s\n", lfn); } else { printf("\n"); } } void extrfile(union tdirent *dir, char *sfn, char *lfn) { FILE *fout; void *buf; long clsize = bootsect.bytesec * bootsect.secclu; long lastclsize; int cluster; struct tm st; struct utimbuf ub; string choice; char *name; int errpf = 0; int skipfile = 0; #ifdef DOS_ATTR unsigned attrib = 0; #endif #ifdef UNIX_ATTR mode_t savumask; #endif if (uselfn) { name = lfn; } else { name = sfn; } if (dir->s.attr & ATTR_DIR) { if (dir->s.name[0] == '.') return; printf("Creating %-40s ", name); err = access(name, 0); if (!err) { printf("Directory already exists\n"); return; } #ifdef __linux__ err = mkdir(name, 0777); #else err = mkdir(name); #endif if (err) { errtot++; printf("%s\a\n", strerror(errno)); } else { printf("OK\n"); } } else { while (1) { int dblbreak = 0; err = access(name, 0); if (err) { break; } else { if (overwry) { break; } if (overwrn) { skipfile = 1; break; } printf("%s already exists. Overwrite it? Yes/No/All/nonE/Rename ", name); while (1) { gets(choice); if (strcasecmp(choice, "y") == 0) { dblbreak = 1; break; } else { if (strcasecmp(choice, "n") == 0) { skipfile = 1; dblbreak = 1; break; } else { if (strcasecmp(choice, "a") == 0) { overwry = 1; dblbreak = 1; break; } else { if (strcasecmp(choice, "e") == 0) { overwrn = 1; skipfile = 1; dblbreak = 1; break; } else { if (strcasecmp(choice, "r") == 0) { printf("Enter new name: "); gets(name); dblbreak = 0; break; } else { printf("Overwrite it? Yes/No/All/nonE/Rename "); }}}}} } if (dblbreak) break; } } if (skipfile) { printf("Skipping %s\n", name); return; } printf("Extracting %-40s ", name); fout = fopen(name, "wb"); if (!fout) { errtot++; printf("%s\a\n", strerror(errno)); return; } cluster = dir->s.clust; if (cluster != 0) { lastclsize = dir->s.size % clsize; if (lastclsize == 0) lastclsize = clsize; buf = malloc(clsize); if (!buf) { printf("Unable to allocate memory\a\n"); exit(1); } do { imgread(f, bootsect.bytesec + bootsect.bytesec * bootsect.fatcps * bootsect.secfat + getdirsize(0) + (cluster-2)*clsize, clsize, buf); cluster = fat[cluster]; if (cluster >= 0xFF8) { if (fwrite(buf, 1, lastclsize, fout) != lastclsize) { errpf++; printf("Error\a\n"); break; } } else { if (fwrite(buf, 1, clsize, fout) != clsize) { errpf++; printf("Error\a\n"); break; } } } while (cluster < 0xFF8); free(buf); } fclose(fout); if (errpf) { errtot++; } else { printf("OK\n"); } st.tm_year = dir->s.dt.year + 80; st.tm_mon = dir->s.dt.mon - 1; st.tm_mday = dir->s.dt.mday; st.tm_hour = dir->s.dt.hour; st.tm_min = dir->s.dt.min; st.tm_sec = dir->s.dt.sec2 * 2; ub.modtime = ub.actime = mktime(&st); err = utime(name, &ub); if (err) { errtot++; printf("Unable to set file timestamp: %s\a\n", strerror(errno)); } #ifdef DOS_ATTR if (dir->s.attr & ATTR_RO) attrib = attrib | FA_RDONLY; if (dir->s.attr & ATTR_HID) attrib = attrib | FA_HIDDEN; if (dir->s.attr & ATTR_SYS) attrib = attrib | FA_SYSTEM; if (dir->s.attr & ATTR_ARC) attrib = attrib | FA_ARCH; err = _dos_setfileattr(name, attrib); if (err) { errtot++; printf("Unable to set file attributes: %s\a\n", strerror(errno)); } #endif #ifdef UNIX_ATTR if (dir->s.attr & ATTR_RO) { savumask = umask(0777); err = chmod(name, 0444 & ~savumask); if (err) { errtot++; printf("Unable to set file attributes: %s\a\n", strerror(errno)); } umask(savumask); } #endif } } void scantree(int cluster, char *dirsfn, char *dirlfn) { union tdirent *dir; union tdirent dirent; long dirsize; int dirread = 0; int i, j, pass; shortstring sfn, tmpext; string lfn; string subdirsfn, subdirlfn; unsigned char cksum; long clsize = bootsect.bytesec * bootsect.secclu; dirsize = getdirsize(cluster); dir = malloc(dirsize); if (!dir) { printf("Unable to allocate memory\a\n"); exit(1); } if (cluster == 0) { imgread(f, bootsect.bytesec + bootsect.bytesec * bootsect.fatcps * bootsect.secfat, dirsize, dir); } else { do { imgread(f, bootsect.bytesec + bootsect.bytesec * bootsect.fatcps * bootsect.secfat + getdirsize(0) + (cluster-2)*clsize, clsize, &dir[dirread/sizeof(union tdirent)]); cluster = fat[cluster]; dirread = dirread + clsize; } while (cluster < 0xFF8); } if (!extr) { if (strcmp(dirsfn, SLASH) != 0) printf("\n"); printf(" Directory of %s\n", dirsfn); if (strcmp(dirsfn, dirlfn)) { printf(" %s\n", dirlfn); } printf("\n"); } for (pass=1; pass<=3; pass++) { strcpy(lfn, ""); for (i=0; i=0; j--) { if (sfn[j] == ' ') { sfn[j] = '\0'; } else { break; } } strncpy(tmpext, dirent.s.ext, 3); tmpext[3] = '\0'; for (j=2; j>=0; j--) { if (tmpext[j] == ' ') { tmpext[j] = '\0'; } else { break; } } if (strlen(tmpext)) { strcat(sfn, "."); strcat(sfn, tmpext); } if (extr) { strlwrl(sfn); if (locase) { strlwrl(lfn); } } strcat(subdirsfn, sfn); if (strlen(lfn)) { strcat(subdirlfn, lfn); } else { strcat(subdirlfn, sfn); if (extr) { strcpy(lfn, sfn); } } if (pass==1) { /* list/create subdirectories */ if (!(dirent.s.attr & ATTR_DIR)) { strcpy(lfn, ""); continue; } if (extr) { if (!extdirs) continue; extrfile(&dirent, &subdirsfn[1], &subdirlfn[1]); } else { dispfile(&dirent, lfn); } } if (pass==2) { /* list/extract regular files */ if (dirent.s.attr & ATTR_DIR) { strcpy(lfn, ""); continue; } if (extr) { if (extdirs) { extrfile(&dirent, &subdirsfn[1], &subdirlfn[1]); } else { extrfile(&dirent, sfn, lfn); } } else { dispfile(&dirent, lfn); } } if (pass==3) { /* recurse subdirectories */ if (!(dirent.s.attr & ATTR_DIR)) { strcpy(lfn, ""); continue; } if (dirent.s.name[0] == '.') { strcpy(lfn, ""); continue; } scantree(dirent.s.clust, subdirsfn, subdirlfn); } strcpy(lfn, ""); } } free(dir); } int main(int argc, char *argv[]) { int i; __u8 dummy; nlsinit(CODEPAGE_LOCAL, CODEPAGE_FAT); setbuf(stdout, NULL); printf("\n"); err = parsecmdline(argc, argv); if (err) { help(); return 1; } if (!forceoffs) { imgoffs = findimg(); if (imgoffs < 0) { return 1; } } imgfmt = chkfmt(); if (imgfmt < 0) { return 1; } if (imgfmt == FMT_DDI) { err = readddimap(); if (err) { return 1; } } err = readbootsec(); if (err) { return 1; } err = readmediaid(); if (err) { return 1; } switch (mediaid) { case MEDIA_D8: bootsect.bytesec = 512; bootsect.secclu = 2; bootsect.fatcps = 2; bootsect.rootent = 112; bootsect.totalsc = 640; bootsect.secfat = 1; bootsect.sectrack = 8; break; case MEDIA_S8: bootsect.bytesec = 512; bootsect.secclu = 1; bootsect.fatcps = 2; bootsect.rootent = 64; bootsect.totalsc = 320; bootsect.secfat = 1; bootsect.sectrack = 8; break; default: ; } if (bootsect.secclu == 0) { printf("It isn't FAT12 disk image\a\n"); return 1; } if (bootsect.totalsc/bootsect.secclu > 4078) { printf("It isn't FAT12 disk image\a\n"); return 1; } if (imgfmt == FMT_DDI) { imgoffs = bootsect.bytesec * bootsect.sectrack; } if (bootsect.fatcps == 2) { err = comparefats(bootsect.bytesec, bootsect.bytesec + bootsect.secfat*bootsect.bytesec, bootsect.secfat*bootsect.bytesec); if (err) { return 1; } } fatentr = bootsect.secfat * bootsect.bytesec / 3 * 2; fat = malloc(fatentr*sizeof(unsigned short)); if (!fat) { printf("Unable to allocate memory\a\n"); exit(1); } err = readfat(fatentr); if (err) { return 1; } f = fopen(imgfile, "rb"); if (!f) { printf("Unable to open %s: %s\a\n", imgfile, strerror(errno)); return 1; } fseek(f, imgoffs + (long) bootsect.bytesec * bootsect.totalsc - 1, SEEK_SET); if (fread(&dummy, 1, sizeof(dummy), f)) { shortened = 0; } else { shortened = 1; } printf(" %s: ", imgfile); if (imgfmt == FMT_DDI) { printf("DiskDupe file, "); } printf("%ldKB disk image ", (long) bootsect.bytesec * bootsect.totalsc / 1024); if (shortened) { printf("(shortened) "); } printf("at offset 0x%lX\n", imgoffs); /* treat volume label containing non-printable characters as invaild */ for (i=0; i