💾 Archived View for mirrors.apple2.org.za › archive › ground.icaen.uiowa.edu › unix › SCIIbin › scii… captured on 2024-07-09 at 03:46:42.

View Raw

More Information

⬅️ Previous capture (2023-01-29)

-=-=-=-=-=-=-

/* @(#) 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<outputfile>] <infiles>                 **
 **                                                                     **
 **           -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 <stdio.h>
#include <sys/types.h>          /* keep sun OS happy                */
#include <sys/stat.h>           /* mostly includes types.h ...      */
#if defined(MSDOS)
#include <memory.h>             /* 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<outputfile>] <infiles>\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 */