Date: Wed, 20 Oct 1993 22:53:52 -0400 (EDT) From: hshubs@BIX.com Subject: Unfolder 2.0 source 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 hshubs@bix.com. * * * #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 (¶mBlock, 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 */