/* @(#) sciibin.c 3.1 30Apr92 */ /************************************************************************* ** ** ** Name : sciibin ** ** Author : Marcel J.E. Mol ** ** Date : 06Mar89 (first release) ** ** Version : 2.00 ** ** Files : sciibin.c Main source file ** ** Purpose : Extract or view BinSCII files ** ** ** ** ------------------------- Revision List ------------------------- ** ** Ver Date Name Remarks ** ** 1.00 06Mar89 Marcel Mol First release ** ** 1.10 27Mar89 Marcel Mol Fished things up, error routine, ** ** linecount, all info fields filled ** ** in, changed info layout. ** ** 2.00 03Jun89 Marcel Mol Bug fixes: alphabet to unsigned ** ** (Dave Whitney) char. Some OS's need types.h. ** ** Fixed CRE/MOD order. ** ** Output filenames can be wrong ** ** when multiple files are ** ** extracted. Made crc and file- ** ** type arrays in seperate includes ** ** 3.00 22Mar90 Marcel Mol Includeed ports of Bruce Kahn ** ** 1.30 27 Feb 90 Bruce Kahn Ported to AOS/VS C 4.01 and MSC 5.1 ** ** Added explicit long definitions ** ** needed. ** ** Redid layouts for dates to be US. ** ** Modified the open routines for MSC ** ** to include binary mode, not text. ** ** Changed the way 8 bit integers are ** ** handled (redone MM). ** ** Added getopt() (see below MM). ** ** 3.10 30Apr92 John Goggan Add -s flag for sending output to ** ** (jgoggan@opus.csv.cmich.edu) Standard Output instead of a file ** ** ** ** ================================================================= ** ** ** ** Compile as follows: ** ** If you don't have getopt, compile and link the given getopt.c ** ** ** ** UNIX: cc sciibin.c -Oso sciibin ** ** ** ** MSC 5.1: cl /c /Ox sciibin.c ** ** (add /DMSDOS, if MSC didn't do it) ** ** link /CO /STACK:10000 sciibin; ** ** ** ** AOS/VS: cc sciibin.c ** ** ccl sciibin ** ** ** ** Defining DEBUG gives some debug information. ** ** Defining DEBUGALL gives input and output of the decode routine. ** ** ** ** Usage: sciibin [-hvtcs] [-o] ** ** ** ** -v show only info on file, do not create output. ** ** -t test file, do not create output. ** ** -c do not check checksums. ** ** -o create given filename instead of the one in ** ** binscii file. Use this only if the input files ** ** contain only one output file. ** ** -s output to standard output instead of a file ** ** -h help. ** ** ** ** ** ** This software is freeware. We can not be held responsible to ** ** any damage this program may coase you or anyone or anything else. ** ** ** ** Mail bugs to: marcel@duteca.tudelft.nl ** ** ** ************************************************************************/ #include #include /* keep sun OS happy */ #include /* mostly includes types.h ... */ #if defined(MSDOS) #include /* For the memset function */ #endif #include "crc.h" #include "filetype.h" #define BUFLEN 256 #define FILENAME_LEN 15 #define BINSCII_HEADER "FiLeStArTfIlEsTaRt\n" #define MAXSEGLEN 0x3000 #if defined(INT8) typedef long int16; #else typedef int int16; #endif /* ==> Expect int variables to be at least 16 bits * Global variables ==> and longs to be at least 32 bits * ==> If not, you have some work to do... */ /* MSDOS has 16 bit ints. */ char buf[BUFLEN+1]; unsigned char dec[BUFLEN+1]; unsigned char alphabet[BUFLEN+1]; /* need $FF in alphabet */ char outfilename[BUFLEN+1]; unsigned char outflag = 0, /* the -o option */ crcflag = 0, /* the -c option */ infoflag = 0, /* the -v option */ testflag = 0, /* the -t option */ soflag = 0, /* the -s option */ makeoutput, /* ! (-t | -v) */ crcok; /* -t | -c */ FILE *outfp; /* output file */ unsigned char filetype, stortype, file_access; /* access -> file_access for MSDOS */ unsigned char modyear, modmonth, modday, modhour, modmin, creyear, cremonth, creday, crehour, cremin; int namlen, linecount, numblocks, auxtype; long filesize, startbyte, segmentlen; char * infilename; char * progname; /* * function declarations */ int main (); int sciibin (); int getheader (); int decode (); int decodestring (); char *myfgets (); void usage (); void error (); char * copyright = "@(#) sciibin.c 3.1 30Apr92 (c) 1989, 1990 M.J.E. Mol"; main(argc, argv) int argc; char **argv; { FILE *fp; int c; extern int optind; /* For getopt */ extern char * optarg; /* For getopt */ int flag; /* Flag for getopt */ progname = *argv; while ((flag = getopt(argc, argv, "hsvcto:")) != EOF) { /* Process options */ switch (flag) { case 'v': infoflag = 1; /* Want only info of file */ break; case 'h': usage(); /* Give help */ exit(0); case 's': soflag = 1; /* Output to Standard Output */ break; case 'c': crcflag = 1; break; case 't': testflag = 1; break; case 'o': #if defined(MSDOS) memset(outfilename, NULL, FILENAME_LEN + 1); #endif strcpy(outfilename, optarg); outflag = 1; break; default : fprintf(stderr,"%s: unknown flag, use -h for help.\n", progname); exit(0); } } makeoutput = !(testflag | infoflag); crcok = testflag | crcflag; #if defined(DEBUG) fprintf(stderr, "make output: %d, crcok: %d\n", makeoutput, crcok); #endif if (optind >= argc) { /* No files given, use stdin */ infilename = "stdin"; linecount = 0; sciibin(stdin); } else while (optind < argc) { infilename = argv[optind]; optind++; if ((fp = fopen(infilename, "r")) == NULL) { perror(infilename); continue; } linecount = 0; sciibin(fp); fclose(fp); } exit(0); } /* main */ /* * Walk over the file processing all segments in it */ sciibin(fp) FILE *fp; { int processed = 0; /* number of processed binscii segments */ int status = 0; /* return codes of calls to decode */ while (myfgets(buf, BUFLEN, fp) != NULL) { #if defined(DEBUG) fprintf(stderr, "(%s) get start:%s", infilename, buf); #endif if (!strncmp(buf, BINSCII_HEADER, strlen(BINSCII_HEADER))) { if (!getheader(fp) && !infoflag) /* if header ok and not -v flag */ status |= decode(fp); processed++; } } if (processed == 0) { error("not a BinSCII file"); return 1; } return status; } /* sciibin */ /* * Build the alphabet, get the output file info and open output file * if necessary. * Still contains lots of debug code to find out the header structure. * (every bit is known now...) */ getheader(fp) FILE *fp; { register int i, j; register int16 crc = 0; struct stat statbuf; /* MUST know if file exists */ char *iomod; /* write or readwrite a file */ /* * Get the alphabet */ if (myfgets(buf, BUFLEN, fp) == NULL) { error("reading alphabet: unexpected end of file"); return 1; } #if defined(DEBUG) fprintf(stderr, "(%s) alphabet:%s", infilename, buf); #endif if (strlen(buf) != 65) { error("alphabet corrupted"); return 1; } /* * Process the alphabet */ for (i = 0; i < BUFLEN; i++) alphabet[i] = 0xff; for (i = 0; i < 64; i++) { j = buf[i]; if (alphabet[j] != 0xff) error("Warning: double character in alphabet"); alphabet[j] = i; } #if defined(DEBUG) for (i = 0; i < BUFLEN; i+=16) { fprintf(stderr, "(%s) alphabet[%3d] =", infilename, i); for (j = 0; j < 16; j++) fprintf(stderr, " %02X", alphabet[i+j]); putc('\n', stderr); } #endif /* * Get the file header */ if (myfgets(buf, BUFLEN, fp) == NULL) { error("reading fileheader: unexpected end of file"); return 1; } #if defined(DEBUG) fprintf(stderr, "(%s) fileheader:%s", infilename, buf); #endif /* * Extract output filename if needed */ if (!outflag) { namlen = *buf - 'A' + 1; /* IS +1 NEEDED ?? */ #if defined(MSDOS) memset(outfilename, NULL, FILENAME_LEN + 1); #endif strncpy(outfilename, buf+1, namlen); outfilename[namlen] = '\0'; } #if defined(DEBUG) fprintf(stderr, "(%s) filename:**%s**\n", infilename, outfilename); fprintf(stderr, "(%s) fileinfo:**%s**", infilename, buf+16); #endif /* * Decode and process the file header information */ if ((i = decodestring(buf+FILENAME_LEN+1, dec)) != 27) error("warning: corrupted file header length"); for (i = 0; i < 24; i++) crc = updcrc(dec[i], crc); if (crc != (dec[24] | (dec[25] << 8))) { if (crcok) error("warning: CRC error in file header"); else { error("error: CRC error in file header"); return 1; } } /* Calculate file length */ filesize = dec[0] + (dec[1]<<8) + ((long) dec[2]<<16); startbyte = dec[3] + (dec[4]<<8) + ((long) dec[5]<<16); /* Calculate file attributes and size */ file_access= dec[6]; filetype = dec[7]; auxtype = dec[8] + (dec[9] << 8); stortype = dec[10]; numblocks = dec[11] + (dec[12]<<8); /* Calculate creation and modification dates */ #define MOD 13 #define CRE 17 /* perhaps creation and modification date are swapped */ creday = dec[CRE] & 0x1f; cremonth = ((dec[CRE+1] & 0x01) << 3) | (dec[CRE] >> 5); creyear = dec[CRE+1] >>1; cremin = dec[CRE+2] & 0x3f; crehour = dec[CRE+3] & 0x1f; modday = dec[MOD] & 0x1f; modmonth = ((dec[MOD+1] & 0x01) << 3) | (dec[MOD] >> 5); modyear = dec[MOD+1] >>1; modmin = dec[MOD+2] & 0x3f; modhour = dec[MOD+3] & 0x1f; segmentlen = dec[21] + (dec[22]<<8) + ((long) dec[23]<<16); if (segmentlen > MAXSEGLEN) error("warning: segmentlen probably to long"); #define READ 0x01 #define WRITE 0x02 #define BACKUP 0x20 #define RENAME 0x40 #define DESTROY 0x80 if (infoflag) { /* Display the files name, type and auxtype */ printf("%-15s %3s aux: $%04X ", outfilename, filetypes[filetype], auxtype); /* Display the file access type */ putchar(file_access & READ ? 'r' : '-'); putchar(file_access & WRITE ? 'w' : '-'); putchar(file_access & RENAME ? 'n' : '-'); putchar(file_access & DESTROY ? 'd' : '-'); putchar(file_access & BACKUP ? 'b' : '-'); /* Display the type of file this is - ProDOS Specific */ switch (stortype) { case 0x0F : printf(" voldir"); break; case 0x0D : printf(" dir"); break; case 0x01 : printf(" seed"); break; case 0x02 : printf(" sap"); break; case 0x03 : printf(" tree"); break; default : printf(" ???"); break; } /* Display modification and creation information */ printf(" %02d/%02d/%02d(%02d:%02d) -", modmonth, modday, modyear, modhour, modmin); printf(" %02d/%02d/%02d(%02d:%02d)\n", cremonth, creday, creyear, crehour, cremin); /* Display segment information */ printf("Part %4d of %4d,", (int) (startbyte / MAXSEGLEN) + 1, (int) ((filesize + MAXSEGLEN-1) / MAXSEGLEN)); printf(" bytes %7ld to %7ld of %7ld bytes, %5d blocks\n", startbyte, startbyte+segmentlen, filesize, numblocks); } /* if (infoflag) */ if (makeoutput && !soflag) { /* will creating output, not just information so verify the access */ #if defined(MSDOS) iomod = (stat(outfilename, &statbuf) == 0) ? "r+b" : "wb"; #else iomod = (stat(outfilename, &statbuf) == 0) ? "r+" : "w"; #endif if ((outfp = fopen(outfilename, iomod)) == NULL) { error("unable to open output file"); perror(outfilename); return 1; } fseek(outfp, startbyte, 0); } /* if (makeoutput) */ return 0; } /* getheader */ /* * Do the actual decoding of the bin data. */ decode(fp) FILE *fp; { register int i; register int crc = 0; int len; crc = 0; while (segmentlen > 0) { if (myfgets(buf, BUFLEN, fp) == NULL) { error("reading file: unexpected end of file"); return 1; } #if defined(DEBUG) fprintf(stderr, "(%s) data:%s", infilename, buf); #endif if ((len = decodestring(buf, dec)) != 48) error("warning: corrupted line length"); for (i = 0; i < 48; i++) crc = updcrc(dec[i], crc); if (makeoutput){ for (i = 0; (i < len) && (segmentlen > 0); i++, segmentlen--) if (!soflag) putc(dec[i], outfp); else fprintf(stdout, "%c", dec[i]); }else segmentlen -= len; #if defined(DEBUG) fprintf(stderr, "(%s) still need %d bytes\n", infilename, segmentlen); #endif } /* * must be at end of segment now, with one remaining line containing * the crc check. */ if (myfgets(buf, BUFLEN, fp) == NULL) { error("reading file crc: unexpected end of file"); return 1; } #if defined(DEBUG) fprintf(stderr, "(%s) crc:%s", infilename, buf); #endif if ((len = decodestring(buf, dec)) != 3) error("warning: corrupted crc length"); if (crc != (dec[0] | (dec[1] << 8))) { if (crcok) error("warning: CRC error in file data"); else { error("error: CRC error in file data"); return 1; } } fclose(outfp); return 0; } /* decode */ /* * Decode one string off scii characters to binary data, meanwhile * calculating crc. */ decodestring(in, out) register char *in; register unsigned char *out; { register int len = 0; #if defined(DEBUGALL) char *b; fprintf(stderr, "(%s) decode in: %s\n", infilename, in); b = in; while (*b) fprintf(stderr, ".%02X", alphabet[*b++]); putc('\n', stderr); b = out; #endif while (strlen(in) > 3) { *out++ = ((alphabet[in[3]] << 2) | (alphabet[in[2]] >> 4)) & 0xFF; *out++ = ((alphabet[in[2]] << 4) | (alphabet[in[1]] >> 2)) & 0xFF; *out++ = ((alphabet[in[1]] << 6) | (alphabet[in[0]])) & 0xFF; len += 3; in += 4; } *out = '\0'; if (*in != '\0' && *in != '\n') error("warning: line not ended by NULL or NEWLINE"); #if defined(DEBUGALL) fprintf(stderr, "(%s) decode out:\n", infilename); while (b != out) fprintf(stderr, ".%02X", *b++); putc('\n', stderr); #endif return len; } /* decodestring */ char *myfgets(buf, len, fp) char *buf; int len; FILE *fp; { linecount++; return fgets(buf, len, fp); } /* myfgets */ void usage() { fprintf(stderr, "Usage: sciibin [-hvtcs] [-o] \n\n"); fprintf(stderr, " -v show only info on file, do not create output.\n"); fprintf(stderr, " -t test file, do not create output.\n"); fprintf(stderr, " -c do not check checksums.\n"); fprintf(stderr, " -o create given filename instead of the one in\n"); fprintf(stderr, " binscii file. Use this only if the input files\n"); fprintf(stderr, " contain only one output file.\n"); fprintf(stderr, " -s output to standard output instead of a file.\n"); fprintf(stderr, " -h this help message.\n"); } /* usage */ void error(str) char *str; { fprintf(stderr, "%s (%s, %d): %s\n", progname, infilename, linecount, str); } /* error */