From: [email protected]
Subject: Unfolder 2.0 source
To: [email protected]

This is the source code for Unfolder 2.0, so it might be named unfolder20.c,
and put in the same directory as unfolder20.hqx.

/*
* The Unfolder - program to reconstitute MacBinary (II) encoded files.
*
* by various BIXen in the 'mac.hack/tutorial', started on 25 December 1989
*
* Originally written by Don Sample, Howard Shubs, and Bob Perkins on
* the BYTE Information eXchange (BIX), this program is intended to allow
* people with a C compiler and no way to transfer resources to their Mac
* to create a way to deal with Mac files which have been downloaded to
* non-Macintosh hardware.  This program will, when told to Unfold,
* reconstitute a MacBinary version 1 or 2 file to its original state.
*
* The program will work as-is under THINK C 5.0.2, with just the MacTraps
* and ANSI-small libraries in the project file with it.  If you are using
* other environments, you may need to modify the file.  If possible,
* please use conditional compilation and send a diffs file to Howard Shubs
* at [email protected].
*
*
*  #includes
*/
/*#include "Headers"*/
#include <stdio.h>
#include <stdarg.h>

#include <Traps.h>
#include <Errors.h>
#include <Types.h>
#include <GestaltEqu.h>
#include <OSUtils.h>
#include <Memory.h>
#include <Quickdraw.h>
#include <ToolUtils.h>
#include <Fonts.h>
#include <OSEvents.h>
#include <Menus.h>
#include <Events.h>
#include <Windows.h>
#include <Files.h>
#include <Resources.h>
#include <Dialogs.h>
#include <StandardFile.h>
#include <Desk.h>
#include <Finder.h>
#include <Pascal.h>
#include <TextEdit.h>


/*
* #defines
*/

#define ALL_TYPES                       -1L
#define BASE_RES_ID                     400
#define NIL_POINTER                     0L
#define MOVE_TO_FRONT           -1L
#define REMOVE_ALL_EVENTS       0
#define MIN_SLEEP                       0
#define NIL_MOUSE_REGION        0L
#define SUSPEND_RESUME_BIT      0x0001
#define RESUMING                        1

#define APPLE_MENU_ID           128
#define FILE_MENU_ID            129
#define EDIT_MENU_ID            130

#define ABOUT_ITEM                      1
#define UNFOLD_ITEM                     1
#define QUIT_ITEM                       2

#define MACBINARY_VERSION       129
#define SCROLL_WIDTH            15
#define NIL_STRING                      "\p"

#define NUM_MASTER_BLOCKS       1               /* this number is semi-arbitrary */

#define BF_OWN_APPL                     0x0002  /* Some finder info flags not defined */
#define F_INITED                        0x0100  /* in FileMgr.h */
#define F_CHANGED                       0x0200
#define F_BUSY                          0x0400
/*
*      MacBinary II Header #defines
*/

#define OLD_V_N                 0
#define LEN_F_N                 1
#define FILENAME                2
#define FILE_TYPE               65
#define FILE_CREA               69
#define ORIG_F_FLAGS    73
#define ZERO_FILL_I             74
#define FILE_V_POS              75
#define FILE_H_POS              77
#define WIN_FOL_ID              79
#define PROTECTED               81
#define ZERO_FILL_II    82
#define LEN_D_FORK              83
#define LEN_R_FORK              87
#define F_CREA_DATE             91
#define F_MOD_DATE              95
#define LEN_GET_INFO    99
#define FINDER_FLAGS    101
#define LEN_TOT_F               116
#define LEN_SEC_H               120
#define MBII_VERS_UP    122
#define MBII_VERS_READ  123
#define CRC                             124

/*
*      Structures
*/

typedef struct Header                           /* MacBinary header data */
       {
       unsigned char   oldVN;                  /* old version number */
       char                    fName[64];              /* file name */
       FInfo                   finderInfo;             /* Original Finder information record */
       unsigned char   protected;
       long                    lenDF;                  /* length of data fork */
       long                    lenRF;                  /* length of resource fork */
       long                    fCreaDate;              /* file creation date */
       long                    fModDate;               /* file's last modified date */
       int                             lenGetInfo;             /* length of Get Info comment */
       unsigned char   finderFlags;    /* Low byte of finder flags */
       long                    lenTotF;                /* length of total files for unpacking */
       int                             lenSecH;                /* length of any secondary header */
       unsigned char   mbIIVersUp;             /* Version of MB II used for upload */
       unsigned char   mbIIVersRead;   /* Version of MB II needed to read file */
       int                             crc;
       } Header;

typedef struct DialogItem                       /* Dialog item */
{
       long                    pointer;
       Rect                    boundsRect;
       unsigned char   itemType;
       unsigned char   itemData[3];
} DialogItem;

typedef struct DialogList                       /* DITL record */
{
       short                   itemCount;
       DialogItem              items[2];
} DialogList;


/*
*      Function Prototypes:
*/

void                    AboutAppl (void);
/*short int             CalcCRC(unsigned char *dataBuf, short int size);*/
short int               CalcCRC(register unsigned char *dataBuf, register int size);
OSErr                   CreateFile(Str255 fName, int vRefNum, OSType creator,
                                OSType type);
Rect                    DeviceRect (void);
int                             GetAnEvent(int eventMask, EventRecord *event, long sleep,
                                RgnHandle mouseRgn);
void                    HandleAppleChoice (short theItem);
void                    HandleFileChoice (short theItem);
void                    HandleEditChoice (short theItem);
void                    HandleEvent (EventRecord *theEvent);
void                    HandleMenuChoice (long menuChoice);
void                    HandleMouseDown (EventRecord *thisEvent);
unsigned char   HeaderCheck (short int, Header *);
void                    Init (void);
void                    MessageDialog(char *format, ...);
void                    Pause(void);
void                    ProcessFile (void);
void                    ToolBoxInit(void);
int                             TrapAvailable(unsigned trapNumber, int trapType);
void                    Unfold (short int refNum, Header *thisHeader,
                                SFReply *outputFile);
int                             WNEIsImplemented(void);


/*
*  Globals
*
*/
EventRecord             gTheEvent;
MenuHandle              gAppleMenu;
MenuHandle              gFileMenu;
MenuHandle              gEditMenu;
Point                   gWhere;
Boolean                 gDone;
Boolean                 gWNEExists;
short                   gQDVersion;


/*
*      FUNCTION:
*              ToolBoxInit
*
*      PURPOSE:
*              Initialize the ToolBox Managers
*/

void    ToolBoxInit()
{
       InitGraf (&(qd.thePort));
       InitFonts ();
       FlushEvents (everyEvent, REMOVE_ALL_EVENTS);
       InitWindows ();
       InitMenus ();
       TEInit ();
       InitDialogs (NIL_POINTER);
       InitCursor ();
}

/*
*      FUNCTION:
*              Pause
*
*      PURPOSE:
*              Wait for the user to click the mouse, or to press the return
*              or Enter key.
*/

void    Pause()

{
       int                     button;
       EventRecord     myEvent;

       button = false;
       while (!button)
       {
               if (GetAnEvent(everyEvent, &myEvent, 0L, NIL_POINTER))
               {
                       if (myEvent.what == mouseDown)
                               button = true;
                       else if (myEvent.what == keyDown)
                       {
                               if (((myEvent.message & charCodeMask) == 0x0d) ||
                                ((myEvent.message & charCodeMask) == 0x03))
                                       button = true;
                       }
               }
       }
       while (StillDown())
               ;
}

/*
*      FUNCTION:
*              MessageDialog
*
*      PURPOSE:
*              Display an error message and then pause until the user clicks the
*              mouse button
*
*      INPUT:
*              char    *format                 - A NULL terminated format string, using
*                                                                the same rules as printf.
*              ...                                             - Other parameters as specified in the
*                                                                format string
*/

void    MessageDialog(char *format, ...)

{
       char                    messageStr[256];
       va_list                 data;
       OSErr                   thisErr;

       va_start(data, format);
       vsprintf(messageStr, format, data);
       CtoPstr(messageStr);
       ParamText ((ConstStr255Param) messageStr, NIL_STRING, NIL_STRING, NIL_STRING);
       thisErr = CautionAlert (BASE_RES_ID, NIL_POINTER);
       va_end(data);
}

#define _Unimplemented  0xA89F
#define _WaitNextEvent  0xA860

/*
*      FUNCTION:
*              GetAnEvent
*
*      PURPOSE:
*              Get an event from the input queue using whichever is appropriate:
*              GetNextEvent, or WaitNextEvent
*
*      INPUTS:
*              int                     eventMask               - Types of events to get
*              EventRecord     *event                  - Where to put retreived event
*              long            sleep                   - amount of time to surrender to
*                                                                        background tasks
*              RgnHandle       mouseRgn                - Global region containing mouse.
*
*      RETURNS:
*              int                     TRUE if an event was retrieved.
*/

enum            /* Which routine to call: Get... or WaitNextEvent                       */
       {
       notKnown,
       getNext,
       waitNext
       };


int             GetAnEvent(int eventMask, EventRecord *event, long sleep,
                RgnHandle mouseRgn)

{
       int             result;
       static  int             callRoutine = notKnown;

       if (callRoutine == notKnown)
       {
               if (WNEIsImplemented())
                       callRoutine = waitNext;
               else
                       callRoutine = getNext;
       }
       if (callRoutine == waitNext)
               result = WaitNextEvent(eventMask, event, sleep, mouseRgn);
       else
       {
               SystemTask();
               result = GetNextEvent(eventMask, event);
       }
       return(result);
}

/*
*      FUNCTION:
*              TrapAvailable
*
*      PURPOSE:
*              Determine if a specific trap routine is available
*
*      INPUTS:
*              unsigned        tNumber         - trap number
*              int                     tType           - trap type (OS or Toolbox)
*
*      RETURNS:
*              int                     TRUE if trap is available
*                                      false otherwise
*/

TrapAvailable(unsigned tNumber, int tType)

{
       /* Check and see if the trap exists. */

       return(NGetTrapAddress(tNumber, tType) !=
        GetTrapAddress(_Unimplemented));
}

/*
*      FUNCTION:
*              WNEIsImplemented
*
*      PURPOSE:
*              Determine if the WaitNextEvent routine is available
*
*      RETURNS:
*              int             TRUE if WNE is available
*                              FALSE otherwise
*/

WNEIsImplemented()

{

       SysEnvRec theWorld; /* used to check if machine has new traps */

       /*
        *      Since WaitNextEvent and HFSDispatch both have the same trap
        *      number ($60), we can only call TrapAvailable for WaitNextEvent
        *      if we are on a machine that supports separate OS and Toolbox
        *      trap tables. We call SysEnvirons and check if machineType < 0.
        */

       SysEnvirons(1, &theWorld);

       /* Even if we got an error from SysEnvirons, the SysEnvirons glue
          has set up machineType. */

       if (theWorld.machineType < 0)
       {
               /* this ROM doesn't have separate trap tables or WaitNextEvent */
               return(false);
       }
       else
       {
               /* check for WaitNextEvent */
               return(TrapAvailable(_WaitNextEvent, ToolTrap));
       }
}

/*
*      FUNCTION:
*              CreateFile
*
*      PURPOSE:
*              Create a file, deleting any previous version if necessary.
*
*      INPUTS:
*              Str255  fName           - File name
*              int             vRefNum         - Directory to create file in
*              OSType  creator         - File's creator ID
*              OSType  type            - File type
*
*      RETURNS:
*              OSErr   - noErr if successful.
*/

OSErr   CreateFile(Str255 fName, int vRefNum, OSType creator, OSType type)

{
       OSErr   thisErr;

       thisErr = Create (fName, vRefNum, creator, type);
       if (thisErr == dupFNErr)
       {
               thisErr = FSDelete (fName, vRefNum);
               if (thisErr == noErr)
                       thisErr = Create (fName, vRefNum, creator, type);
       }
       return (thisErr);
}

/*
*      FUNCTION:
*              CalcCRC
*
*      PURPOSE:
*              Calculate a CCITT CRC for a data buffer
*
*      INPUTS:
*              unsigned char   *data   - pointer to data buffer to perform CRC on
*              int                             size    - size of data buffer
*
*      RETURNS:
*              int                             - CRC for data buffer.  If data buffer contains as its
*                                                last 2 bytes the CRC for the previous bytes the return
*                                                value is 0
*/

#define CCITT_CRC_GEN   0x1021

short int       CalcCRC(register unsigned char *dataBuf, register int size)

{
       register unsigned short crc = 0;
       register unsigned short dataByte;
       register int    i;

       while (size--)
       {
               dataByte = *dataBuf++ << 8;
               for (i = 8; i > 0; i--)
               {
                       if ((dataByte ^ crc) & 0x8000)
                               crc = (crc << 1) ^ CCITT_CRC_GEN;
                       else
                               crc <<= 1 ;
                       dataByte <<= 1;
               }
       }
       return(crc);
}


/*
*      FUNCTION:
*              AboutAppl
*
*      PURPOSE:
*              Tell about the program
*
*/

void AboutAppl (void)
{
       Rect            bounds = {58, 108, 192, 400};
       WindowPtr       thisWindow;
       GrafPtr         oldPort;

       GetPort (&oldPort);
       thisWindow = NewWindow (NIL_POINTER, &bounds, "\p", true, 1, (WindowPtr) -1L,
                                                       false, 0L);
       bounds = thisWindow->portRect;
       SetPort (thisWindow);
       /*                123456789   0   123   4   567890123456789   0123456789        */
       TextBox ("Unfolder\015\015by\015\015Howard S Shubs\015Don Sample\015Bob Perkins", 51L, &bounds, 1);
       Pause ();
       SetPort (oldPort);
       DisposeWindow (thisWindow);
}


/*
*      FUNCTION:
*              DeviceRect
*
*      PURPOSE:
*              Get the bounds rect of the main screen.
*
*      RETURNS:
*              Rect    the bounds rect of the main screen.
*
*/

Rect DeviceRect (void)
{
       GDHandle                device;
       Rect                    devRect;

       if (gQDVersion > 0)
       {
               device = GetMainDevice ();
               devRect = (**(device)).gdRect;
       }
       else
               devRect = qd.screenBits.bounds;

       return devRect;
}


/*
*      FUNCTION:
*              HandleAppleChoice
*
*      PURPOSE:
*              Deal with selection from the Apple menu
*
*      INPUTS:
*              short int       theItem: contains the selection from the menu
*
*/

void HandleAppleChoice (short theItem)
{
       Str255          accName;
       int                     accNumber;
       short int       itemNumber;
       DialogPtr       AboutDialog;

       switch (theItem)
       {
               case ABOUT_ITEM:
                       AboutAppl ();
                       break;
               default:
                       GetItem (gAppleMenu, theItem, accName);
                       accNumber = OpenDeskAcc (accName);
                       break;
       }
}

/*
*      FUNCTION:
*              HandleEditChoice
*
*      PURPOSE:
*              Deal with selection from the Edit menu by passing them to the
*              system.
*
*      INPUTS:
*              short int       theItem: contains the selection from the menu
*
*/

void HandleEditChoice (short theItem)
{
       if (!SystemEdit (theItem - 1))
               ;
}


/*
*      FUNCTION:
*              HandleFileChoice
*
*      PURPOSE:
*              Deal with selection from the File menu
*
*      INPUTS:
*              short int       theItem: contains the selection from the menu
*
*/
/*{}{}{}*/
void HandleEvent (EventRecord *thisEvent)
{
       char    theChar;

       if (gWNEExists)
               WaitNextEvent (everyEvent, thisEvent, MIN_SLEEP, NIL_MOUSE_REGION);
       else
       {
               SystemTask ();
               GetNextEvent (everyEvent, thisEvent);
       }

       switch (thisEvent->what)
       {
               case nullEvent:
                       break;
               case mouseDown:
                       HandleMouseDown (thisEvent);
                       break;
               case keyDown:
               case autoKey:
                       theChar = (thisEvent->message) & charCodeMask;
                       if (( (thisEvent->modifiers) & cmdKey) != 0)
                               HandleMenuChoice (MenuKey(theChar));
                       break;
               case updateEvt:
/*                      BeginUpdate ((WindowPtr) (thisEvent->message) );
                       UpdateWindow ((WindowPtr) (thisEvent->message));
                       EndUpdate ((WindowPtr) (thisEvent->message) );*/
                       break;
               case app4Evt:
                       if ( ( (thisEvent->message) & SUSPEND_RESUME_BIT) == RESUMING)
                       {
/*                                      gState.applFlags.isSuspended =
                                                                       ((thisEvent->message) & 0x01) == 0;*/
                       }
                       break;
       }
}

/*
*      FUNCTION:
*              HandleFileChoice
*
*      PURPOSE:
*              Deal with selection from the File menu
*
*      INPUTS:
*              short int       theItem: contains the selection from the menu
*
*/

void HandleFileChoice (short theItem)
{
       switch (theItem)
       {
               case UNFOLD_ITEM:
                       ProcessFile ();
                       break;
               case QUIT_ITEM:
                       gDone = true;
                       break;
       }
}

/*
*      FUNCTION:
*              HandleMenuChoice
*
*      PURPOSE:
*              Deal with selection of menu items
*
*      INPUTS:
*              long int        menuChoice: contains the selection from a menu
*
*      RETURNS:
*              unsigned char                           - TRUE if the file is MacBinary
*
*/

void HandleMenuChoice (long menuChoice)
{
       short   theMenu;
       short   theItem;

       if (menuChoice != 0)
       {
               theMenu = HiWord (menuChoice);
               theItem = LoWord (menuChoice);
               switch (theMenu)
               {
                       case APPLE_MENU_ID:
                               HandleAppleChoice (theItem);
                               break;
                       case FILE_MENU_ID:
                               HandleFileChoice (theItem);
                               break;
                       case EDIT_MENU_ID:
                               HandleEditChoice (theItem);
                               break;
               }
               HiliteMenu (0);
       }
}

/*
*      FUNCTION:
*              HandleMouseDown
*
*      PURPOSE:
*              Deal with mouseDown events.
*
*      INPUTS:
*              EventRecord     *thisEvent   points to the current event record.
*
*/

void HandleMouseDown (EventRecord *thisEvent)
{
       Rect                    devRect;
       long                    menuChoice;
       short int               thePart;
       WindowPeek              window;
       long                    windSize;

       thePart = FindWindow ((thisEvent->where), (WindowPtr *) &window);
       switch (thePart)
       {
               case inMenuBar:
                       menuChoice = MenuSelect ((thisEvent->where));
                       HandleMenuChoice (menuChoice);
                       break;
               case inSysWindow:
                       SystemClick (thisEvent, (WindowPtr) window);
                       break;
               case inDrag:
                       devRect = DeviceRect ();
                       DragWindow ((WindowPtr) window, (thisEvent->where), &devRect);
                       break;
               case inGrow:
                       devRect = DeviceRect ();
                       windSize = GrowWindow ((WindowPtr) window, (thisEvent->where),
                                                                &devRect);
                       if (windSize > 0)
                       {
                               Rect    bad;
                               Point   p;

                               /* invalidate the part of the window with the grow icon */
                               bad = (window->port).portRect;
                               bad.top = bad.bottom - SCROLL_WIDTH;
                               bad.left = bad.right - SCROLL_WIDTH;
                               InvalRect (&bad);
                       }
                       SizeWindow ((WindowPtr) window, LoWord (windSize),
                                               HiWord (windSize), true);
                       break;
               case inGoAway:
                       /*if (TrackGoAway ((WindowPtr) window, (thisEvent->where) ) )
                               DoCloseWindow ((WindowPtr) window);*/
                       break;
       }
}

/*
*      FUNCTION:
*              HeaderCheck
*
*      PURPOSE:
*              Look at a selected file to determine if it is a MacBinary (II)
*              file or not.
*
*      INPUTS:
*              short int               refNum          - reference number of the opened file to be
*                                                                        inspected.
*              Header                  *thisHeader     - MacBinary header record to return data in.
*
*      RETURNS:
*              unsigned char                           - TRUE if the file is MacBinary
*
*/

unsigned char HeaderCheck (short int refNum, Header *thisHeader)
{
       long int                count = 128;
       OSErr                   thisErr;
       short int               i;
       unsigned char   *buffer;
       unsigned char   soFarSoGood = false;

       /* Go to the beginning of the file pointed to by refNum and
          read the first
(count) bytes. */
       thisErr = SetFPos (refNum, fsFromStart, 0L);
       if (thisErr == noErr)
       {
               buffer = (unsigned char *) NewPtr (count);
               if ((thisErr = MemError()) == noErr)
               {
                       thisErr = FSRead (refNum, &count, buffer);
                       if (thisErr == noErr)
                       {
                               /* Transfer the data read from the file into thisHeader */
                               /* First, check to make sure that the mbz bytes ARE zero */
                               if (buffer[OLD_V_N] == 0 && buffer[ZERO_FILL_I] == 0)
                                       soFarSoGood = true;

                               /* Transfer the file name, and make sure its length is > 0 */
                               if (buffer[LEN_F_N] > 0 && buffer[LEN_F_N] < 64 && soFarSoGood)
                               {
                                       BlockMove (&buffer[LEN_F_N], &(thisHeader->fName),
                                                               buffer[LEN_F_N]+1);

                                       /* Get the finderInfo */
                                       BlockMove (&buffer[FILE_TYPE], &(thisHeader->finderInfo),
                                                               sizeof(FInfo));

                                       /* Get the "protected" flag */
                                       thisHeader->protected = buffer[PROTECTED];

                                       /* Get the fork sizes */
                                       BlockMove (&buffer[LEN_D_FORK], &(thisHeader->lenDF),
                                                               8);

                                       /* Get file's dates */
                                       BlockMove (&buffer[F_CREA_DATE], &(thisHeader->fCreaDate),
                                                               8);

                                       /* Get length of "Get Info" comment */
                                       BlockMove (&buffer[LEN_GET_INFO], &(thisHeader->lenGetInfo),
                                                               2);

                                       /* Low Finder flags */
                                       thisHeader->finderFlags = buffer[FINDER_FLAGS];

                                       /* Total size of files */
                                       BlockMove (&buffer[LEN_TOT_F], &(thisHeader->lenTotF),
                                                               4);

                                       /* Get length of secondary header */
                                       BlockMove (&buffer[LEN_SEC_H], &(thisHeader->lenSecH),
                                                               2);

                                       /* Get the versions of MacBinary II */
                                       thisHeader->mbIIVersUp = buffer[MBII_VERS_UP];
                                       thisHeader->mbIIVersRead = buffer[MBII_VERS_READ];

                                       /* Get the CRC of the header */
                                       BlockMove (&buffer[CRC], &(thisHeader->crc), 2);

                                       soFarSoGood = (CalcCRC(buffer, 124)
                                                                                       == thisHeader->crc)
                                                               && (buffer[MBII_VERS_READ] == MACBINARY_VERSION);

                                       if (!soFarSoGood && (buffer[ZERO_FILL_II] == 0))
                                       {
                                               /* Make more comparisons to make sure that this
                                                  is really a MacBinary I file. */
                                               for (i = 101; buffer[i]==0 && i < 126; i++);

                                               /* If "i" is >= 126, the loop above completed
                                                  without finding a non-zero, so this is MacBinary I.  */
                                               if (soFarSoGood = i >= 126)
                                               {
                                                       /* It is, it really _is_ MacBinary I */

                                                       /* Low Finder flags */
                                                       thisHeader->finderFlags = 0;

                                                       /* Total size of files */
                                                       thisHeader->lenTotF = 0;

                                                       /* Get length of secondary header */
                                                       thisHeader->lenSecH = 0;

                                                       /* Get the versions of MacBinary II */
                                                       thisHeader->mbIIVersUp = MACBINARY_VERSION;
                                                       thisHeader->mbIIVersRead = MACBINARY_VERSION;

                                                       /* Get the CRC of the header */
                                                       thisHeader->crc = 0;
                                               }
                                       }

                               }
                               else
                                       soFarSoGood = false;
                       };
                       DisposPtr ((Ptr) buffer);
                       thisErr = MemError ();
               }
       }
       if (thisErr != noErr)
               MessageDialog("Error: %d reading MacBinary header", thisErr);
       else if (!soFarSoGood)
               MessageDialog("This is not a MacBinary file");
       return soFarSoGood;
}


/*
*      FUNCTION:
*              Unfold
*
*      PURPOSE:
*              Restore the forks of a MacBinary encoded file.
*
*      INPUTS:
*              short int               refNum          - reference number of the opened file to be
*                                                                        used as input.
*              Header                  *thisHeader - MacBinary header information
*              SFReply                 *thisFile       - Information needed to access the created
*                                                                        file.
*
*      RETURNS:
*              unsigned char                           - TRUE if the file is MacBinary
*              Header                  *thisHeader     - MacBinary header record to return data in.
*
*/

void Unfold (short int refNum, Header *thisHeader, SFReply *thisFile)
{
       char            *buffer;
       char            bufferAllocated;
       long int        buffSize;
       long int        forkSize;
       long int        offSet;
/*      long int        grow;*/
       short int       outRefNum;              /* reference number for the output file */
       OSErr           thisErr;                /* status code */

       /* Allocate memory for a buffer by this rule:
               Never allocate less than MIN_BUFFER, or more than MAX_BUFFER.
               Between those two, allocate what's available less LEAVE_FREE,
               as long as that isn't less than MIN_BUFFER. */
#define MIN_BUFFER      4*1024L
#define MAX_BUFFER      256*1024L
#define LEAVE_FREE      32*1024L
       buffSize = MaxBlock ();  /*MaxMem (&grow);*/
       buffSize = buffSize > MAX_BUFFER + LEAVE_FREE ?
                               MAX_BUFFER :
                               buffSize < MIN_BUFFER ?
                                       MIN_BUFFER :
                                       buffSize - LEAVE_FREE < MIN_BUFFER ?
                                               MIN_BUFFER :
                                               buffSize - LEAVE_FREE;

       buffer = NewPtr (buffSize);
       bufferAllocated = (thisErr = MemError()) == noErr;

       if (!bufferAllocated)
               MessageDialog ("Couldn't allocate a work buffer.  Give this program more memory, then run it again.");

       /* Is there a data fork?  If there is, rebuild it. */
       if ((thisHeader->lenDF > 0) && bufferAllocated)
       {
               Ptr             source;
               Size    count;
               Str255  dest;

               source = (Ptr) &((*thisFile).fName);
               count = (char) *source;
               count = BitAnd (count, 0x000000ffL) + 1L;
               BlockMove (source, dest, count);        /* {}{}{} */

               forkSize = thisHeader->lenDF;
               if ((thisErr = FSOpen (dest, thisFile->vRefNum, &outRefNum))
                == noErr)
               {
                       /* position beyond the MacBinary header */
                       if ((thisErr = SetFPos (refNum, fsFromStart, 128)) == noErr)
                       {
                               while (thisErr == noErr && forkSize > buffSize)
                               {
                                       thisErr = FSRead (refNum, &buffSize, buffer);
                                       if (thisErr == noErr)
                                               thisErr = FSWrite (outRefNum, &buffSize, buffer);
                                       forkSize -= buffSize;
                               }
                               if (thisErr == noErr)
                               {
                                       thisErr = FSRead(refNum, &forkSize, buffer);
                                       if (thisErr == noErr)
                                               thisErr = FSWrite (outRefNum, &forkSize, buffer);
                               }
                       }
                       if (thisErr == noErr)
                       {
                               thisErr = FSClose (outRefNum);
                               if (thisErr == noErr)
                                       thisErr = FlushVol (NIL_POINTER, thisFile->vRefNum);
                               else
                                       FlushVol (NIL_POINTER, thisFile->vRefNum);
                       }
                       else
                       {
                               FSClose (outRefNum);
                               FlushVol (NIL_POINTER, thisFile->vRefNum);
                       }
               }
       }

       /* Is there a resource fork?  If there is, rebuild it. */
       if ((thisHeader->lenRF > 0) && bufferAllocated)
       {
               Ptr             source;
               Size    count;
               Str255  dest;

               source = (Ptr) &((*thisFile).fName);
               count = (char) *source;
               count = BitAnd (count, 0x000000ffL) + 1L;
               BlockMove (source, dest, count);        /* {}{}{} */

               forkSize = thisHeader->lenRF;
               if ((thisErr = OpenRF (dest, thisFile->vRefNum, &outRefNum))
                == noErr)
               {
                       /* position beyond the MacBinary header and the Data Fork */
                       offSet = (128 - (thisHeader->lenDF % 128)) & 0x7f;
                       if ((thisErr = SetFPos (refNum, fsFromStart,
                        (thisHeader->lenDF) + offSet + 128)) == noErr)
                       {
                               while (thisErr == noErr && forkSize > buffSize)
                               {
                                       thisErr = FSRead (refNum, &buffSize, buffer);
                                       if (thisErr == noErr)
                                               thisErr = FSWrite (outRefNum, &buffSize, buffer);
                                       forkSize -= buffSize;
                               }
                               if (thisErr == noErr)
                               {
                                       thisErr = FSRead(refNum, &forkSize, buffer);
                                       if (thisErr == noErr)
                                               thisErr = FSWrite (outRefNum, &forkSize, buffer);
                               }
                       }
                       if (thisErr == noErr)
                       {
                               thisErr = FSClose (outRefNum);
                               if (thisErr == noErr)
                                       thisErr = FlushVol (NIL_POINTER, thisFile->vRefNum);
                               else
                                       FlushVol (NIL_POINTER, thisFile->vRefNum);
                       }
                       else
                       {
                               FSClose (outRefNum);
                               FlushVol (NIL_POINTER, thisFile->vRefNum);
                       }
               }
       }
       if (bufferAllocated)
               DisposPtr (buffer);
       if (thisErr != noErr)
               MessageDialog("Error: %d unfolding MacBinary file", thisErr);
}


/*
*      FUNCTION:
*              ProcessFile
*
*      PURPOSE:
*              Determine our input and output files, then do the processing.
*
*/

void ProcessFile (void)
{
       Boolean                 running;
       int                             i;
       SFTypeList              typeList;
       SFReply                 inputFile, outputFile;
       Header                  mbHeader;
       OSErr                   thisErr;
       short int               refNum;
       ParamBlockRec   paramBlock;
       unsigned int    finderFlags;

       SFGetFile (gWhere, NIL_POINTER, NIL_POINTER, ALL_TYPES, typeList,
                       NIL_POINTER, &inputFile);
       running = inputFile.good == true;

       if (running)
       {
               if ((thisErr = FSOpen (inputFile.fName, inputFile.vRefNum,
                &refNum)) != noErr)
               {
                       PtoCstr((unsigned char *) inputFile.fName);
                       MessageDialog("Error: %d opening file %s", thisErr, inputFile.fName);
               }

               /* read the MacBinary header.  If it's okay, decode the file. */
               else if (HeaderCheck (refNum, &mbHeader))
               {
                       Str255  *string = (Str255 *) &(mbHeader.fName);

                       SFPutFile (gWhere, NIL_POINTER, *string,
                                       NIL_POINTER, &outputFile);
                       if (outputFile.good)
           {
               if ((thisErr = CreateFile(outputFile.fName,
                outputFile.vRefNum, mbHeader.finderInfo.fdCreator,
                                mbHeader.finderInfo.fdType)) != noErr)
                               {
                                       PtoCstr((unsigned char *) outputFile.fName);
                                       MessageDialog("Error: %d creating file %s", thisErr,
                                        outputFile.fName);
                                       running = false;
                               }
                               else
                               {
                                       /* copy data & resource forks from input file */
                                       Unfold (refNum, &mbHeader, &outputFile);

                                       paramBlock.fileParam.ioCompletion = NIL_POINTER;
                                       paramBlock.fileParam.ioNamePtr = (StringPtr) &outputFile.fName;
                                       paramBlock.fileParam.ioVRefNum = outputFile.vRefNum;
                                       paramBlock.fileParam.ioFVersNum = 0;
                                       /* Set the finder Info for the file */
                                       paramBlock.fileParam.ioFlFndrInfo = mbHeader.finderInfo;
                                       finderFlags = mbHeader.finderInfo.fdFlags;
                                       finderFlags |= mbHeader.finderFlags;
                                       finderFlags &= ~(fOnDesk | BF_OWN_APPL | F_INITED |
                                        F_CHANGED | F_BUSY);
                                       paramBlock.fileParam.ioFlFndrInfo.fdFlags = finderFlags;
                                       paramBlock.fileParam.ioFlFndrInfo.fdFldr = 0;
                                       paramBlock.fileParam.ioFlFndrInfo.fdLocation.h = 0;
                                       paramBlock.fileParam.ioFlFndrInfo.fdLocation.v = 0;
                                       /* Set the Creation and Modification dates on the file */
                                       paramBlock.fileParam.ioFlCrDat = mbHeader.fCreaDate;
                                       paramBlock.fileParam.ioFlMdDat = mbHeader.fModDate;
                                       thisErr = PBSetFInfo (&paramBlock, false);
                                       if (thisErr != noErr)
                                               MessageDialog("Error: %d, setting file attributes", thisErr);
                               }
                       }
               }
               thisErr = FSClose (refNum);
       }
}


/*
*      FUNCTION:
*              Init
*
*      PURPOSE:
*              Initialize the environment, establish menus, and set variables.
*
*  SIDE EFFECTS:
*              Initializes the environment, establishes menus, and initializes
*              the global variables.
*
*/

void Init (void)
{
       long                    response;
       Boolean                 gestaltExists;
       Boolean                 sysEnvironsExists;
       SysEnvRec               *theWorld;
       OSErr                   thisErr;
       Handle                  thisHandle;
       DialogList              errorDITL;
       AlertTemplate   thisAlert;


   /* Initialize everything                           */
   /* Init the toolbox routines */
       ToolBoxInit();

       /* Make menus */
       gAppleMenu = NewMenu (APPLE_MENU_ID, "\p\024");
       gFileMenu = NewMenu (FILE_MENU_ID, "\pFile");
       gEditMenu = NewMenu (EDIT_MENU_ID, "\pEdit");
       InsertMenu (gAppleMenu, 0);
       InsertMenu (gFileMenu, 0);
       InsertMenu (gEditMenu, 0);

       /* Add items to menus */
       AppendMenu (gAppleMenu, "\pAbout Unfolder...;(-");
       AddResMenu (gAppleMenu, 'DRVR');
       AppendMenu (gFileMenu, "\pUnfold.../U;Quit/Q");
       AppendMenu (gEditMenu, "\pUndo/Z;(-;Cut/X;Copy/C;Paste/X;Clear");

       /* Draw the menu bar */
       DrawMenuBar ();

       /* Specify the point at which the SF dialogs will appear */
       gWhere.v = 100;
       gWhere.h = 100;

       /* We're not done yet */
       gDone = false;

       /* Can we call WaitNextEvent? */
       gWNEExists = WNEIsImplemented();

       /* What kind of Quickdraw do we have? */
       gestaltExists = TrapAvailable (_GestaltDispatch, ToolTrap);
       sysEnvironsExists = TrapAvailable (_SysEnvirons, OSTrap);
       if (gestaltExists)
               thisErr = Gestalt ('qd  ', &response);
       else
       {
               if (sysEnvironsExists)
               {
                       theWorld = (SysEnvRec *) NewPtr (sizeof (SysEnvRec));
                       if (theWorld == NIL_POINTER)
                       {
                               thisErr = MemError ();
                               MessageDialog("Error: %d allocating memory", thisErr);
                       }
                       thisErr = SysEnvirons (1, theWorld);
                       response = (long) theWorld->hasColorQD;
                       DisposPtr ((Ptr) theWorld);
               }
               else
                       response = 0L;
       }
       gQDVersion = LoWord (response) >> 8;


       /* Do the miscellaneous resources exist yet?  Check for one.  If
          that doesn't exist, assume that none of them do and create them. */
       thisHandle = GetResource ('ALRT', BASE_RES_ID);
       if (thisHandle == NIL_POINTER)
       {
               /* Construct the error DITL    see IM I-427 */
               errorDITL.itemCount = 1;                /* total of two items */

               /* OK button */
               errorDITL.items[0].pointer = 0L;
               errorDITL.items[0].boundsRect.top = 96;
               errorDITL.items[0].boundsRect.left = 137;
               errorDITL.items[0].boundsRect.bottom = 116;
               errorDITL.items[0].boundsRect.right = 195;
               errorDITL.items[0].itemType = (char) btnCtrl | ctrlItem;
               errorDITL.items[0].itemData[0] = 2;
               errorDITL.items[0].itemData[1] = 'O';
               errorDITL.items[0].itemData[2] = 'K';

               /* Static Text */
               errorDITL.items[1].pointer = 0L;
               errorDITL.items[1].boundsRect.top = 13;
               errorDITL.items[1].boundsRect.left = 65;
               errorDITL.items[1].boundsRect.bottom = 82;
               errorDITL.items[1].boundsRect.right = 318;
               errorDITL.items[1].itemType = (char) statText;
               errorDITL.items[1].itemData[0] = 2;
               errorDITL.items[1].itemData[1] = '^';
               errorDITL.items[1].itemData[2] = '0';

               /* Make the DITL into a resource */
               thisHandle = NewHandle (sizeof (DialogList));
               BlockMove (&errorDITL, *thisHandle, sizeof (DialogList));
               AddResource (thisHandle, 'DITL', BASE_RES_ID, "\pError DITL");
               thisErr = ResError ();


               /* Construct the error ALRT   IM-I 426 */
               thisAlert.boundsRect.top = 40;
               thisAlert.boundsRect.left = 40;
               thisAlert.boundsRect.bottom = 172;
               thisAlert.boundsRect.right = 368;
               thisAlert.itemsID = BASE_RES_ID;
               thisAlert.stages = 0x7775;

               /* Make the ALRT into a resource */
               thisHandle = NewHandle (sizeof (AlertTemplate));
               BlockMove (&thisAlert, *thisHandle, sizeof (AlertTemplate));
               AddResource (thisHandle, 'ALRT', BASE_RES_ID, "\pError ALRT");
               thisErr = ResError ();
       }
       else
               ReleaseResource (thisHandle);
}


main()
{
       int                             i;

       /* Maximize available memory */
       MaxApplZone ();

   /* Be sure to have plenty of master pointer blocks */
   for (i = 0; i < NUM_MASTER_BLOCKS; i++)
       MoreMasters();

       Init ();

       while (!gDone)
       {
               HandleEvent (&gTheEvent);
       }

} /* main */