BuildISO.c

/*
    File:       BuildISO.c
    
    Description:Try to build a ISO 9660 floppy disc, interactively.
                Currently only builds the Primary Volume Descriptor
                and puts files at the root level.  Does not do
                subdirectories.
 
    Author:     BB
 
    Copyright:  Copyright: © 1988-1999 by Apple Computer, Inc.
                all rights reserved.
    
    Disclaimer: You may incorporate this sample code into your applications without
                restriction, though the sample code has been provided "AS IS" and the
                responsibility for its operation is 100% yours.  However, what you are
                not permitted to do is to redistribute the source as "DSC Sample Code"
                after having made changes. If you're going to re-distribute the source,
                we require that you make it clear in the source that the code was
                descended from Apple Sample Code, but that you've made changes.
    
    Change History (most recent first):
                6/24/99 Updated for Metrowerks Codewarror Pro 2.1(KG)
                1/94    Converted to Universal Headers.  Fixed a bug which prevented    
                        Apple extensions from working correctly.
                5/90    Modified for d e v e l o p and Think C 4.0
                7/1/88  Original Version for the Macintosh(BB)
 
*/
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <Files.h>
#include <OSUtils.h>
#include <Memory.h>
#include <Errors.h>
#include <strings.h>
#include <Devices.h>
#include <MacMemory.h>
 
 
#include "HighSierra.h"
#include "BuildISO.h"
 
#define FLOPPY_SIZE 0x186       /* size in 2k blocks of a 800k floppy */
 
#include "ErrorMsg.h"
#include "i_o.h"
#include "Support.h"
#include "MyDialog.h"
 
Str255          nullStr = "\p";
Str255          rootName = "\p\000";
Str255          parentName = "\p\001";
 
/************************************************************************
 *
 *  Function:       CreatePVD
 *
 *  Purpose:        create the contents of the Primary Volume Descriptor.
 *
 *  Returns:        void
 *
 *  Side Effects:   adds to the output file.
 *
 *  Description:    go through all the primary volume descriptor, showing
 *                  each field in all it's glory.  We don't bother showing
 *                  the extra blanks at the end of each string field.
 *
 *
 ************************************************************************/
 
OSErr
CreatePVD(short referenceNumber)
{
    PVD     p;
    OSErr   result;
    long    offset;
    char    volID[33];
    Boolean goOn;
    
    ClearOut((char *)&p, sizeof(p));
    p.VDType = 1;
    p.VSStdId[0] = 'C';
    p.VSStdId[1] = 'D';
    p.VSStdId[2] = '0';
    p.VSStdId[3] = '0';
    p.VSStdId[4] = '1';
    p.VSStdVersion = 1;
    CharCopy(p.systemIdentifier, "Apple Computer, Inc., Type:0001", sizeof(p.systemIdentifier));
 
    goOn = AskForString((char *)"\pWhat do you want to call this volume? (32 characters or less)", volID);
    if (goOn == false)
        return -1;
 
    NormalizeVolumeName(volID);
    CharCopy(p.volumeIdentifier, volID, sizeof(p.volumeIdentifier));
 
 
    p.lsbVolumeSpaceSize = NormalizeLong((long)FLOPPY_SIZE);
    p.msbVolumeSpaceSize = (long)FLOPPY_SIZE;
    p.lsbVolumeSetSize = NormalizeWord(FLOPPY_SIZE);
    p.msbVolumeSetSize = FLOPPY_SIZE;
    p.lsbVolumeSetSequenceNumber = NormalizeWord(1);
    p.msbVolumeSetSequenceNumber = 1;
    
    p.lsbLogicalBlockSize = NormalizeWord(CDBLKSIZE);
    p.msbLogicalBlockSize = CDBLKSIZE;
                
 
    p.lsbPathTableSize = NormalizeLong(PATHTBLSIZE);
    p.msbPathTableSize = PATHTBLSIZE;
    p.lsbPathTable1 = NormalizeLong(LSBPATH);
    p.msbPathTable1 = MSBPATH;
    
    p.lsbPathTable2 = 0L;
    p.msbPathTable2 = 0L;
    
    /* Exercise for reader: get the time via GetTime() and convert
    ** to string of the format shown below for these strange dates.
    ** Use that date and time to fill the various volume date fields.
    ** The date shown is my daughter's birth date and time...
    */
    CharCopy(p.volumeCreation, "19870914060100000", sizeof(p.volumeCreation));
    CharCopy(p.volumeModification, "19870914060100000", sizeof(p.volumeModification));
    CharCopy(p.volumeExpiration, "00000000000000000", sizeof(p.volumeExpiration));
    CharCopy(p.volumeEffective, "19870914060100000", sizeof(p.volumeEffective));
 
    p.FileStructureStandardVersion = 1;
    
    SpaceOut(p.volumeSetIdentifier, sizeof(p.volumeSetIdentifier));
    SpaceOut(p.publisherIdentifier, sizeof(p.publisherIdentifier));
    SpaceOut(p.dataPreparerIdentifier, sizeof(p.dataPreparerIdentifier));
    SpaceOut(p.applicationIdentifier, sizeof(p.applicationIdentifier));
    SpaceOut(p.copyrightFileIdentifier, sizeof(p.copyrightFileIdentifier));
    SpaceOut(p.abstractFileIdentifier, sizeof(p.abstractFileIdentifier));
    SpaceOut(p.bibliographicFileIdentifier, sizeof(p.bibliographicFileIdentifier));
 
    CreateDirRcd((DirRcd *)&p.rootDirectoryRecord, rootName, 
        DIRECTORY, CDBLKSIZE, (short) directoryBit, 0L, 0L, 0);
    
    p.Reserved1 = 0;
    ClearOut(p.Reserved2, sizeof(p.Reserved2));
    ClearOut(p.Reserved3, sizeof(p.Reserved3));
    p.Reserved4 = 0;
 
#ifdef VERBOSE  /* if I want to verify what I've done */
    DumpPVD(&p);
#endif
    offset = (long) HSVOLSTART * (long) CDBLKSIZE;
    result = isoWrite(referenceNumber, (Ptr)&p, (long) sizeof(p), (long)offset);
    if (result != noErr)
        ErrorMsg("CreatePVD: isoWrite() returned %d", result);
    else
        ErrorMsg("volume descriptors successfully created.");
    return result;
}
 
 
/************************************************************************
 *
 *  Function:       CreateVDT
 *
 *  Purpose:        create the contents of the Volume Descriptor Terminator
 *
 *  Returns:        void
 *
 *  Side Effects:   adds to the output file.
 *
 *  Description:    Build a simple VDT, fill it in, and write it out to
 *                  a famous place.
 *
 *
 ************************************************************************/
 
OSErr
CreateVDT(short referenceNumber)
{
    PVD     p;
    OSErr   result;
    long    offset;
    
    ClearOut((char *)&p, sizeof(p));
    p.VDType = 255;
    p.VSStdId[0] = 'C';
    p.VSStdId[1] = 'D';
    p.VSStdId[2] = '0';
    p.VSStdId[3] = '0';
    p.VSStdId[4] = '1';
    p.VSStdVersion = 1;
    offset = (long) HSTERMSTART * (long) CDBLKSIZE;
    result = isoWrite(referenceNumber, (Ptr)&p, (long) sizeof(p), (long)offset);
    if (result != noErr)
        ErrorMsg("CreateVDT: isoWrite() returned %d", result);
    return result;
}
 
 
 
 
/************************************************************************
 *
 *  Function:       CreatePathTable
 *
 *  Purpose:        create path tables
 *
 *  Returns:        nothing
 *
 *  Side Effects:   writes lsb path table and msb path table
 *
 *  Description:    We'll assume just the root.  Dump out the path
 *                  path table in both formats.  We'll put the
 *                  path table in famous spots.
 *
 ************************************************************************/
OSErr
CreatePathTable(short referenceNumber)
{
    char    buffer[CDBLKSIZE];
    PathTableRecordPtr  d;
    long    offset;
    OSErr   result;
    
    ClearOut(buffer, sizeof(buffer));
    d = (PathTableRecordPtr) &buffer[0];
    
    d->len_di = 1;
    d->XARlength = 0;
    d->dirLocation = NormalizeLong(DIRECTORY);
    d->parentDN = 0;
    
    offset = LSBPATH * (long) CDBLKSIZE;
    result = isoWrite(referenceNumber, buffer, (long) CDBLKSIZE, offset);
    if (result != noErr)
        ErrorMsg("CreatePathTable: isoWrite() returned %d", result);
    
    d->len_di = 1;
    d->dirLocation = DIRECTORY;
    d->parentDN = 0;
    
    offset = MSBPATH * (long) CDBLKSIZE;
    result = isoWrite(referenceNumber, buffer, (long) CDBLKSIZE, offset);
    if (result != noErr)
        ErrorMsg("CreatePathTable: isoWrite() returned %d", result);
    return result;
}
 
 
/************************************************************************
 *
 *  Function:   CreateDirRcd
 *
 *  Purpose:    Create a directory record for a file
 *
 *  Returns:    none
 *
 *  Side Effects:   fills *d with directory information.  We assume
 *                  caller has allocated space for d.
 *
 *  Description:
 *
 ************************************************************************/
void
CreateDirRcd(DirRcd *d, StringPtr name, long start, long length, short flags, OSType fType, OSType fCreator, short finderFlags)
{
    Ptr     dPtr;
    DateTimeRec today;
 
    d->XARlength = 0;
    d->lsbStart = NormalizeLong(start);
    d->msbStart = start;
    d->lsbDataLength = NormalizeLong(length);
    d->msbDataLength = length;
    d->fileFlags = flags;
    if (finderFlags & fInvisible)
        d->fileFlags |= existenceBit;
    GetTime(&today);
    d->year = today.year-1900;
    d->month = today.month;
    d->day = today.day;
    d->hour = today.hour;
    d->minute = today.minute;
    d->second = today.second;
    d->gmtOffset = 0;
    d->interleaveSize = 0;
    d->interleaveSkip = 0;
    d->lsbVolSetSeqNum = NormalizeWord(1);
    d->msbVolSetSeqNum = 1;
    
    d->len_fi = CreateISOName((char *)d->fi, name);
 
    d->len_dr = 32 + d->len_fi;
    
    AddAppleExtensions(d, fType, fCreator, finderFlags);
    
    if (d->len_dr & 1)  /* odd dirRcds need pad byte */
    {
        dPtr = (char *)d;
        dPtr[d->len_dr] = '\000';
        d->len_dr++;
    }
}
 
 
/************************************************************************
 *
 *  Function:       AddOldAppleExtensions
 *
 *  Purpose:        optionally add apple extensions to ISO 9660
 *
 *  Returns:        void
 *
 *  Side Effects:   directory record may get extended.  Must have enough
 *                  room in area pointed to by dirRcd for this to happen.
 *
 *  Description:    Check the fType.  If it's non-zero, add the information
 *                  necessary for the Apple Extensions to ISO 9660.  Note
 *                  that we can't just assign fType and fCreator, since
 *                  longs are aligned within structures.
 *
 *                  This procedure adds the old, "BA" Apple extensions.
 *
 ************************************************************************/
void
AddOldAppleExtensions(DirRcd *dirRcd, OSType fType, OSType fCreator, short flags)
{
    OldAppleExtension   apple;
    short           i;
    short           j;
    short           limit;
    char            *aPtr;
    Ptr             fPtr;
    
    if (fType != 0L)
    {
        apple.macFlag[0] = 'B';
        apple.macFlag[1] = 'A';
        apple.systemUseID = 06;
        fPtr = (char *)&fType;
        
        for (i = 0; i < 4; i++)
            apple.fileType[i] = fPtr[i];
        fPtr = (char *)&fCreator;
        
        for (i = 0; i < 4; i++)
            apple.fileCreator[i] = fPtr[i];
        
        apple.finderFlags[0] = (flags >> 8) & 0xFF;
        apple.finderFlags[1] = flags & 0xFF;
        
        limit = sizeof(apple);
        
        aPtr = (char *)&apple;
 
        j = dirRcd->len_fi;
        
        if (!(j & 1))       /* 9401 bug fix BL¡B */
        {
            dirRcd->fi[j] = 0;
            j++;    /* there is a pad byte after odd length file names */
        }
        
        for (i = 0; i <= limit; i++)
            dirRcd->fi[i + j] = aPtr[i];
        
        dirRcd->len_dr += limit;
    }
}   
 
 
/************************************************************************
 *
 *  Function:       AddAppleExtensions
 *
 *  Purpose:        optionally add apple extensions to ISO 9660
 *
 *  Returns:        void
 *
 *  Side Effects:   directory record may get extended.  Must have enough
 *                  room in area pointed to by dirRcd for this to happen.
 *
 *  Description:    Check the fType.  If it's non-zero, add the information
 *                  necessary for the Apple Extensions to ISO 9660.  Note
 *                  that we can't just assign fType and fCreator, since
 *                  longs are aligned within structures.
 *
 ************************************************************************/
void
AddAppleExtensions(DirRcd *dirRcd, OSType fType, OSType fCreator, short flags)
{
    AppleExtension  apple;
    short           i;
    short           j;
    short           limit;
    char            *aPtr;
    Ptr             fPtr;
    
    if (fType != 0L)
    {
        apple.signature[0] = 'A';
        apple.signature[1] = 'A';
        apple.extensionLength = 0x0E;
        apple.systemUseID = 02;
        fPtr = (char *)&fType;
        
        for (i = 0; i < 4; i++)
            apple.fileType[i] = fPtr[i];
        fPtr = (char *)&fCreator;
        
        for (i = 0; i < 4; i++)
            apple.fileCreator[i] = fPtr[i];
        
        apple.finderFlags[0] = (flags >> 8) & 0xFF;
        apple.finderFlags[1] = flags & 0xFF;
        
        limit = sizeof(apple);
        
        aPtr = (char *)&apple;
 
        j = dirRcd->len_fi;
        
        if (!(j & 1))       /* 9401 bug fix BL¡B */
        {
            dirRcd->fi[j] = 0;
            j++;    /* there is a pad byte after odd length file names */
        }
        
        for (i = 0; i <= limit; i++)
            dirRcd->fi[i + j] = aPtr[i];
        
        dirRcd->len_dr += limit;
    }
}   
 
 
/************************************************************************
 *
 *  Function:       CopyDirRcdToBuffer
 *
 *  Purpose:        copy directory record to buffer
 *
 *  Returns:        nothing
 *
 *  Side Effects:   buffer is filled a little bit more
 *
 *  Description:    copy a directory record to the buffer.
 *
 ************************************************************************/
void
CopyDirRcdToBuffer(DirRcd *d, char *b)
{
    char    *dPrime;
    short   i;
    
    dPrime = (char *)d;
    for (i = 0; i < d->len_dr; i++)
        *b++ = *dPrime++;
}
 
/************************************************************************
 *
 *  Function:       CopyRsrcFork
 *
 *  Purpose:        copy the resource fork of a file
 *
 *  Returns:        OSErr
 *                      mostly noErr, but could be
 *                      ioErr and the like if isoWrite complains.
 *
 *  Side Effects:   floppy gets new data written on it.
 *
 *  Description:    we have a starting location, "start", and a length.
 *                  Allocate an appropriate buffer and read from the file
 *                  specified by "name" and "vRefNum".  Write that information
 *                  out to the floppy using our isoWrite call.
 *
 ************************************************************************/
OSErr
CopyRsrcFork(short referenceNumber, StringPtr name, short vRefNum, long start, long length)     /* how much to write */
{
    Ptr rsrcBuf;
    ParamBlockRec   pb;
    Boolean     goOn;
    OSErr       result;
    long        physicalLength;
    short       myRefNum;
    
    ClearOut((Ptr)&pb, sizeof(pb));
    goOn = true;
    physicalLength = ROUND_UP(length);
    
    rsrcBuf = NewPtrClear(physicalLength);
    if (rsrcBuf == NULL)
    {
        ErrorMsg("Can't allocate %ld bytes for CopyRsrcFork()", length);
        return mFulErr; /* nothing to clean up */
    }
 
    pb.ioParam.ioCompletion = NULL;
    pb.ioParam.ioNamePtr = name;
    pb.ioParam.ioVRefNum = vRefNum;
    pb.ioParam.ioVersNum = 0;
    pb.ioParam.ioPermssn = fsCurPerm;
    pb.ioParam.ioMisc = NULL;
    result = PBOpenRF(&pb, false);
    if (result != noErr)
    {
        ErrorMsg("CopyRsrcFork: PBOpenRF returned %d", result);
        ErrorMsg("vRefNum %d, name %P", vRefNum, name);
        C2PStr((char *)name);
        goOn = false;
    }
    
    if (goOn)
    {
        myRefNum = pb.ioParam.ioRefNum;
        result = FSRead(myRefNum, &length, rsrcBuf);
        if (result != noErr)
        {
            ErrorMsg("CopyRsrcFork: FSRead returned %d", result);
            goOn = false;
        }
    }
    
    if (goOn)
    {
        result = isoWrite(referenceNumber, rsrcBuf, physicalLength, start);
 
        if (result != noErr)
        {
            ErrorMsg("CopyRsrcFork: isoWrite returned %d", result);
            goOn = false;
        }
    }
        
    PBClose(&pb, false);
    DisposePtr(rsrcBuf);
    return result;
}
 
 
 
/************************************************************************
 *
 *  Function:       CopyDataFork
 *
 *  Purpose:        copy the resource fork of a file
 *
 *  Returns:        OSErr
 *                      mostly noErr, but could be
 *                      ioErr and the like if isoWrite complains.
 *
 *  Side Effects:   floppy gets new data written on it.
 *
 *  Description:    we have a starting location, "start", and a length.
 *                  Allocate an appropriate buffer and read from the file
 *                  specified by "name" and "vRefNum".  Write that information
 *                  out to the floppy using our isoWrite call.
 *
 ************************************************************************/
OSErr
CopyDataFork(short referenceNumber, StringPtr name, short vRefNum, long start, long length)     /* how much to write */
{
    Ptr dataBuf;
    ParamBlockRec   pb;
    Boolean     goOn;
    OSErr       result;
    long        physicalLength;
    short       myRefNum;
    
    ClearOut((Ptr)&pb, sizeof(pb));
    goOn = true;
    physicalLength = ROUND_UP(length);
    
    dataBuf = NewPtrClear(physicalLength);
    if (dataBuf == NULL)
    {
        ErrorMsg("Can't allocate %ld bytes for CopyDataFork()", length);
        return mFulErr; /* nothing to clean up */
    }
    
    pb.ioParam.ioCompletion = NULL;
    pb.ioParam.ioNamePtr = name;
    pb.ioParam.ioVRefNum = vRefNum;
    pb.ioParam.ioVersNum = 0;
    pb.ioParam.ioPermssn = fsCurPerm;
    pb.ioParam.ioMisc = NULL;
    result = PBOpen(&pb, false);
    if (result != noErr)
    {
        ErrorMsg("CopyDataFork: PBOpen returned %d", result);
        ErrorMsg("vRefNum %d, name %P", vRefNum, name);
        C2PStr((char *)name);
        goOn = false;
    }
    
    if (goOn)
    {
        myRefNum = pb.ioParam.ioRefNum;
        result = FSRead(myRefNum, &length, dataBuf);
        if (result != noErr)
        {
            ErrorMsg("CopyDataFork: FSRead returned %d", result);
            goOn = false;
        }
    }
    
    if (goOn)
    {
        result = isoWrite(referenceNumber, dataBuf, physicalLength, start);
 
        if (result != noErr)
        {
            ErrorMsg("CopyDataFork: isoWrite returned %d", result);
            goOn = false;
        }
    }
    
    PBClose(&pb, false);
    DisposePtr(dataBuf);
    return result;
}
 
/************************************************************************
 *
 *  Function:       CreateFiles
 *
 *  Purpose:        create files in the root of a ISO floppy.
 *  
 *  Returns:        nothing
 *
 *  Side Effects:   floppy gets new data in famous root area
 *
 *  Description:    For each file, find the size of the two forks.
 *                  Copy the resource fork first, then the data fork
 *                  (associated files come before data files in ISO)
 *
 ************************************************************************/
void
CreateFiles(short referenceNumber)
{
    StringPtr   name;
    short   vRefNum;
    DirRcd  dirRcd;
    long    start;      /* where we start putting data on the CD */
    long    rsrcLength;
    long    dataLength;
    char    *b;
    OSErr   result;
    OSType  fType;
    OSType  fCreator;
    short   flags;
    short   ISOFlags;
    char    buffer[CDBLKSIZE];
    
    
    ClearOut(buffer, sizeof(buffer));
    b = &buffer[0];
    name = (StringPtr)NewPtr(255);
    if (name == NULL)
    {
        ErrorMsg("Can't allocate 255 bytes for a string.");
        return;
    }
 
    CreateDirRcd(&dirRcd, rootName, DIRECTORY, CDBLKSIZE, (short) directoryBit, 0L, 0L, 0);
    CopyDirRcdToBuffer(&dirRcd, b);
    b += dirRcd.len_dr;
 
    CreateDirRcd(&dirRcd, parentName, DIRECTORY, CDBLKSIZE, (short) directoryBit, 0L, 0L, 0);
    CopyDirRcdToBuffer(&dirRcd, b);
    b += dirRcd.len_dr;
 
    start = DATASTART * CDBLKSIZE;
 
    /* Keep asking for names, even if errors occur.  Most errors will be because
     * The user tried to copy too big of a file to the floppy. 
     */
    while (HFSFile(name, &vRefNum) == true)
    {
        result = GetFileInfo(name, vRefNum, &rsrcLength, &dataLength, &fType, &fCreator, &flags);
        if (result != noErr)
            ErrorMsg("Can't get file information for %s", name);
        else
        {
            ISOFlags = (flags & fInvisible) ? existenceBit : 0;
    
            if (rsrcLength != 0L)
            {
                CreateDirRcd(&dirRcd, name, start/CDBLKSIZE, rsrcLength, ISOFlags | associatedBit,
                    fType, fCreator, flags);
    
                result = CopyRsrcFork(referenceNumber, name, vRefNum, start, rsrcLength);
                if (result == noErr)
                {
                    CopyDirRcdToBuffer(&dirRcd, b);
                    b += dirRcd.len_dr;
                    start = ROUND_UP(start+rsrcLength);
                }
                else
                    ErrorMsg("Failed to copy resource fork.");
        
            }
    
            if (result == noErr)
            {
                CreateDirRcd(&dirRcd, name, start/CDBLKSIZE, dataLength, ISOFlags, 
                    fType, fCreator, flags);
        
                result = CopyDataFork(referenceNumber, name, vRefNum, start, dataLength);
                if (result == noErr)
                {
                    CopyDirRcdToBuffer(&dirRcd, b);
                    b += dirRcd.len_dr;
                    start = ROUND_UP(start+dataLength);
                }
                else
                    ErrorMsg("Failed to copy data fork.");
        
                ClearOut((Ptr)name, 255);
            }
        }
    }
 
    result = isoWrite(referenceNumber, (Ptr)buffer, (long)sizeof(buffer), (long) (DIRECTORY*CDBLKSIZE));
    if (result != noErr)
        ErrorMsg("CreateDataFiles: isoWrite of directory records returned %d", result);
    DisposePtr((Ptr)name);
}