1 /* SCCS Id: @(#)mrecover.c 3.4 1996/07/24 */
2 /* Copyright (c) David Hairston, 1993. */
3 /* NetHack may be freely redistributed. See license for details. */
5 /* Macintosh Recovery Application */
7 /* based on code in util/recover.c. the significant differences are:
8 * - GUI vs. CLI. the vast majority of code here supports the GUI.
9 * - Mac toolbox equivalents are used in place of ANSI functions.
10 * - void restore_savefile(void) is event driven.
11 * - integral type substitutions here and there.
15 * Think C 5.0.4 project specs:
17 * SIZE (-1) info: flags: 0x5880, size: 65536L/65536L (64k/64k)
18 * libraries: MacTraps [yes], MacTraps2 (HFileStuff) [yes], ANSI [no]
19 * compatibility: system 6 and system 7
20 * misc: sizeof(int): 2, "\p": unsigned char, enum size varies,
21 * prototypes required, type checking enforced, no optimizers,
22 * FAR CODE [no], FAR DATA [no], SEPARATE STRS [no], single segment,
23 * short macsbug symbols
27 * To do (maybe, just maybe):
28 * - Merge with the code in util/recover.c.
29 * - Document launch (e.g. GUI equivalent of 'recover basename').
31 * - Internal memory tweaks (stack and heap usage).
32 * - Use status file to allow resuming aborted recoveries.
33 * - Bundle 'LEVL' files with recover (easier document launch).
34 * - Prohibit recovering games "in progress".
35 * - Share AppleEvents with NetHack to auto-recover crashed games.
41 /**** Toolbox defines ****/
43 /* MPW C headers (99.44% pure) */
46 #include <Resources.h>
55 #include <Notification.h>
58 #include <StandardFile.h>
59 #include <ToolUtils.h>
60 #include <Processes.h>
62 #include <TextUtils.h>
64 #ifdef THINK /* glue for System 7 Icon Family call (needed by Think C 5.0.4) */
65 pascal OSErr GetIconSuite(Handle *theIconSuite, short theResID, long selector)
66 = {0x303C, 0x0501, 0xABC9};
70 /**** Application defines ****/
73 typedef struct memBytes /* format of 'memB' resource, preloaded/locked */
76 short memCleanup; /* 4 - memory monitor activity limit */
77 long memPreempt; /* 32k - start iff FreeMem() > */
78 long memWarning; /* 12k - warn if MaxMem() < */
79 long memAbort; /* 4k - abort if MaxMem() < */
80 long memIOBuf; /* 16k - read/write buffer size */
81 } memBytes, *memBytesPtr, **memBytesHandle;
83 #define membID 128 /* 'memB' resource ID */
87 #define CURS_FRAME 4L /* 1/15 second - spin cursor */
88 #define CURS_LATENT 60L /* pause before spin cursor */
89 #define curs_Init (-1) /* token for set arrow */
90 #define curs_Total 8 /* maybe 'acur' would be better */
91 #define cursorOffset 128 /* GetCursor(cursorOffset + i) */
98 mbarAppl, /* normal mode */
99 mbarRecover, /* in recovery mode */
100 mbarDA /* DA in front mode */
127 /* standard minimum required Edit menu */
134 alrtNote, /* general messages */
135 alrtHelp, /* help message */
138 alertAppleMenu = 127, /* menuItem to alert ID offset */
143 #define aboutBufSize 80 /* i.e. 2 lines of 320 pixels */
147 #define nmBufSize (32 + aboutBufSize + 32)
148 typedef struct notifRec
151 struct notifRec * nmNext;
153 unsigned char nmBuf[nmBufSize];
154 } notifRec, *notifPtr;
156 #define nmPending nmRefCon /* &in.Notify */
157 #define iconNotifyID 128
158 #define ics_1_and_4 0x00000300
178 typedef struct modeFlags
180 short Front; /* fg/bg event handling */
181 short Notify; /* level of pending NM notifications */
182 short Dialog; /* a modeless dialog is open */
183 short Recover; /* restoration progress index */
186 /* convenient define to allow easier (for me) parsing of 'vers' resource */
187 typedef struct versXRec
191 unsigned char versStr[]; /* (small string)(large string) */
192 } versXRec, *versXPtr, **versXHandle;
196 /**** Global variables ****/
197 modeFlags in = {1}; /* in Front */
200 unsigned char aboutBuf[aboutBufSize]; /* vers 1 "Get Info" string */
201 memBytesPtr pBytes; /* memory management */
202 unsigned short memActivity; /* more memory management */
203 MenuHandle mHnd[menu_Total];
204 CursPtr cPtr[curs_Total]; /* busy cursors */
205 unsigned long timeCursor; /* next cursor frame time */
206 short oldCursor = curs_Init; /* see adjustGUI() below */
207 notifPtr pNMQ; /* notification queue pointer */
208 notifRec nmt; /* notification template */
209 DialogTHndl thermoTHnd;
210 DialogRecord dlgThermo; /* progress thermometer */
211 #define DLGTHM ((DialogPtr) &dlgThermo)
212 #define WNDTHM ((WindowPtr) &dlgThermo)
213 #define GRFTHM ((GrafPtr) &dlgThermo)
215 Point sfGetWhere; /* top left corner of get file dialog */
216 Ptr pIOBuf; /* read/write buffer pointer */
217 short vRefNum; /* SFGetFile working directory/volume refnum */
218 long dirID; /* directory i.d. */
219 NMUPP nmCompletionUPP; /* UPP for nmCompletion */
220 FileFilterUPP basenameFileFilterUPP; /* UPP for basenameFileFilter */
221 UserItemUPP drawThermoUPP; /* UPP for progress callback */
223 #define MAX_RECOVER_COUNT 256
225 #define APP_NAME_RES_ID (-16396) /* macfile.h */
226 #define PLAYER_NAME_RES_ID 1001 /* macfile.h */
228 /* variables from util/recover.c */
229 #define SAVESIZE FILENAME
230 unsigned char savename[SAVESIZE]; /* originally a C string */
231 unsigned char lock[256]; /* pascal string */
233 int hpid; /* NetHack (unix-style) process i.d. */
234 short saveRefNum; /* save file descriptor */
235 short gameRefNum; /* level 0 file descriptor */
236 short levRefNum; /* level n file descriptor */
239 /**** Prototypes ****/
240 static void warmup(void);
241 static Handle alignTemplate(ResType, short, short, short, Point *);
242 pascal void nmCompletion(NMRec *);
243 static void noteErrorMessage(unsigned char *, unsigned char *);
244 static void note(short, short, unsigned char *);
245 static void adjustGUI(void);
246 static void adjustMemory(void);
247 static void optionMemStats(void);
248 static void RecoverMenuEvent(long);
249 static void eventLoop(void);
250 static void cooldown(void);
252 pascal void drawThermo(WindowPtr, short);
253 static void itemizeThermo(short);
254 pascal Boolean basenameFileFilter(ParmBlkPtr);
255 static void beginRecover(void);
256 static void continueRecover(void);
257 static void endRecover(void);
258 static short saveRezStrings(void);
260 /* analogous prototypes from util/recover.c */
261 static void set_levelfile_name(long);
262 static short open_levelfile(long);
263 static short create_savefile(unsigned char *);
264 static void copy_bytes(short, short);
265 static void restore_savefile(void);
267 /* auxiliary prototypes */
268 static long read_levelfile(short, Ptr, long);
269 static long write_savefile(short, Ptr, long);
270 static void close_file(short *);
271 static void unlink_file(unsigned char *);
283 /* manager initialization */
284 InitGraf(&qd.thePort);
291 nmCompletionUPP = NewNMProc(nmCompletion);
292 basenameFileFilterUPP = NewFileFilterProc(basenameFileFilter);
293 drawThermoUPP = NewUserItemProc(drawThermo);
295 /* get system environment, notification requires 6.0 or better */
296 (void) SysEnvirons(curSysEnvVers, &sysEnv);
297 if (sysEnv.systemVersion < 0x0600)
299 ParamText("\pAbort: System 6.0 is required", "\p", "\p", "\p");
300 (void) Alert(alidNote, (ModalFilterUPP) 0L);
307 /* normally these routines are never reached from here */
318 /* pre-System 7 MultiFinder hack for smooth launch */
319 for (i = 0; i < 10; i++)
321 if (WaitNextEvent(osMask, &wnEvt, 2L, (RgnHandle) 0L))
322 if (((wnEvt.message & osEvtMessageMask) >> 24) == suspendResumeMessage)
323 in.Front = (wnEvt.message & resumeFlag);
327 /* clear out the Finder info */
329 short message, count;
331 CountAppFiles(&message, &count);
333 ClrAppFiles(count--);
337 /* fill out the notification template */
338 nmt.nmr.qType = nmType;
340 nmt.nmr.nmSound = (Handle) -1L; /* system beep */
341 nmt.nmr.nmStr = nmt.nmBuf;
342 nmt.nmr.nmResp = nmCompletionUPP;
343 nmt.nmr.nmPending = (long) &in.Notify;
348 /* get the app name */
350 ProcessSerialNumber psn;
352 info.processInfoLength = sizeof(info);
353 info.processName = nmt.nmBuf;
354 info.processAppSpec = NULL;
355 GetCurrentProcess(&psn);
356 GetProcessInformation(&psn, &info);
359 /* prepend app name (31 chars or less) to notification buffer */
364 GetAppParms(* (Str255 *) &nmt.nmBuf, &apRefNum, &apParams);
368 /* add formatting (two line returns) */
369 nmt.nmBuf[++(nmt.nmBuf[0])] = '\r';
370 nmt.nmBuf[++(nmt.nmBuf[0])] = '\r';
372 /**** note() is usable now but not aesthetically complete ****/
374 /* get notification icon */
375 if (sysEnv.systemVersion < 0x0700)
377 if (! (nmt.nmr.nmIcon = GetResource('SICN', iconNotifyID)))
378 note(nilHandleErr, 0, "\pNil SICN Handle");
382 if (GetIconSuite(&nmt.nmr.nmIcon, iconNotifyID, ics_1_and_4))
383 note(nilHandleErr, 0, "\pBad Icon Family");
386 /* load and align various dialog/alert templates */
387 (void) alignTemplate('ALRT', alidNote, 0, 4, (Point *) 0L);
388 (void) alignTemplate('ALRT', alidHelp, 0, 4, (Point *) 0L);
390 thermoTHnd = (DialogTHndl) alignTemplate('DLOG', dlogProgress, 20, 8, (Point *) 0L);
392 (void) alignTemplate('DLOG', getDlgID, 0, 6, (Point *) &sfGetWhere);
394 /* get the "busy cursors" (preloaded/locked) */
395 for (i = 0; i < curs_Total; i++)
399 if (! (cHnd = GetCursor(i + cursorOffset)))
400 note(nilHandleErr, 0, "\pNil CURS Handle");
405 /* get the 'vers' 1 long (Get Info) string - About Recover... */
409 if (! (vHnd = (versXHandle) GetResource('vers', 1)))
410 note(nilHandleErr, 0, "\pNil vers Handle");
412 i = (**vHnd).versStr[0] + 1; /* offset to Get Info pascal string */
414 if ((aboutBuf[0] = (**vHnd).versStr[i]) > (aboutBufSize - 1))
415 aboutBuf[0] = aboutBufSize - 1;
419 MoveHHi((Handle) vHnd); /* DEE - Fense ... */
420 HLock((Handle) vHnd);
421 BlockMove(&((**vHnd).versStr[i]), &(aboutBuf[1]), aboutBuf[0]);
422 ReleaseResource((Handle) vHnd);
425 /* form the menubar */
426 for (i = 0; i < menu_Total; i++)
428 if (! (mHnd[i] = GetMenu(i + muidApple)))
429 note(nilHandleErr, 0, "\pNil MENU Handle");
431 /* expand the apple menu */
433 AddResMenu(mHnd[menuApple], 'DRVR');
435 InsertMenu(mHnd[i], 0);
438 /* pre-emptive memory check */
440 memBytesHandle hBytes;
443 if (! (hBytes = (memBytesHandle) GetResource('memB', membID)))
444 note(nilHandleErr, 0, "\pNil Memory Handle");
448 if (MaxMem(&grow) < pBytes->memPreempt)
449 note(memFullErr, 0, "\pMore Memory Required\rTry adding 16k");
451 memActivity = pBytes->memCleanup; /* force initial cleanup */
454 /* get the I/O buffer */
455 if (! (pIOBuf = NewPtr(pBytes->memIOBuf)))
456 note(memFullErr, 0, "\pNil I/O Pointer");
459 /* align a window-related template to the main screen */
461 alignTemplate(ResType rezType, short rezID, short vOff, short vDenom, Point *pPt)
466 vOff += GetMBarHeight();
468 if (! (rtnHnd = GetResource(rezType, rezID)))
469 note(nilHandleErr, 0, "\pNil Template Handle");
471 pRct = (Rect *) *rtnHnd;
473 /* don't move memory while aligning rect */
474 pRct->right -= pRct->left; /* width */
475 pRct->bottom -= pRct->top; /* height */
476 pRct->left = (qd.screenBits.bounds.right - pRct->right) / 2;
477 pRct->top = (qd.screenBits.bounds.bottom - pRct->bottom - vOff) / vDenom;
479 pRct->right += pRct->left;
480 pRct->bottom += pRct->top;
483 *pPt = * (Point *) pRct; /* top left corner */
488 /* notification completion routine */
490 nmCompletion(NMRec * pNMR)
492 (void) NMRemove(pNMR);
494 (* (short *) (pNMR->nmPending))--; /* decrement pending note level */
495 ((notifPtr) pNMR)->nmDispose = 1; /* allow DisposPtr() */
499 * handle errors inside of note(). the error message is appended to the
500 * given message but on a separate line and must fit within nmBufSize.
503 noteErrorMessage(unsigned char *msg, unsigned char *errMsg)
505 short i = nmt.nmBuf[0] + 1; /* insertion point */
507 BlockMove(&msg[1], &nmt.nmBuf[i], msg[0]);
508 nmt.nmBuf[i + msg[0]] = '\r';
509 nmt.nmBuf[0] += (msg[0] + 1);
511 note(memFullErr, 0, errMsg);
515 * display messages using Notification Manager or an alert.
516 * no run-length checking is done. the messages are created to fit
517 * in the allocated space (nmBufSize and aboutBufSize).
520 note(short errorSignal, short alertID, unsigned char *msg)
526 if (MaxMem(&grow) < pBytes->memAbort)
527 noteErrorMessage(msg, "\pOut of Memory");
530 if (errorSignal || !in.Front)
533 short i = nmt.nmBuf[0] + 1; /* insertion point */
535 if (errorSignal) /* use notification template */
539 /* we're going to abort so add in this prefix */
540 BlockMove("Abort: ", &nmt.nmBuf[i], 7);
544 else /* allocate a notification record */
546 if (! (pNMR = (notifPtr) NewPtr(sizeof(notifRec))))
547 noteErrorMessage(msg, "\pNil New Pointer");
551 pNMR->nmr.nmStr = (StringPtr) &(pNMR->nmBuf);
553 /* update the notification queue */
560 /* find the end of the queue */
561 for (pNMX = pNMQ; pNMX->nmNext; pNMX = pNMX->nmNext)
568 /* concatenate the message */
569 BlockMove(&msg[1], &((pNMR->nmBuf)[i]), msg[0]);
570 (pNMR->nmBuf)[0] += msg[0];
572 in.Notify++; /* increase note pending level */
574 NMInstall((NMRec *) pNMR);
582 /* in front and no error so use an alert */
583 ParamText(msg, "\p", "\p", "\p");
584 (void) Alert(alertID, (ModalFilterUPP) 0L);
593 static short oldMenubar = mbar_Init; /* force initial update */
595 WindowPeek frontWindow;
597 /* oldCursor is external so it can be reset in endRecover() */
598 static short newCursor = curs_Init;
599 unsigned long timeNow;
602 /* adjust menubar 1st */
603 newMenubar = in.Recover ? mbarRecover : mbarAppl;
605 /* desk accessories take precedence */
606 if (frontWindow = (WindowPeek) FrontWindow())
607 if (frontWindow->windowKind < 0)
610 if (newMenubar != oldMenubar)
613 switch (oldMenubar = newMenubar)
616 EnableItem(mHnd[menuFile], mitmOpen);
617 SetItemMark(mHnd[menuFile], mitmOpen, noMark);
618 DisableItem(mHnd[menuFile], mitmClose_DA);
619 DisableItem(mHnd[menuEdit], 0);
623 DisableItem(mHnd[menuFile], mitmOpen);
624 SetItemMark(mHnd[menuFile], mitmOpen, checkMark);
625 DisableItem(mHnd[menuFile], mitmClose_DA);
626 DisableItem(mHnd[menuEdit], 0);
630 DisableItem(mHnd[menuFile], mitmOpen);
631 EnableItem(mHnd[menuFile], mitmClose_DA);
632 EnableItem(mHnd[menuEdit], 0);
639 /* now adjust the cursor */
640 if (useArrow = (!in.Recover || (newMenubar == mbarDA)))
641 newCursor = curs_Init;
642 else if ((timeNow = TickCount()) >= timeCursor) /* spin cursor */
644 timeCursor = timeNow + CURS_FRAME;
645 if (++newCursor >= curs_Total)
649 if (newCursor != oldCursor)
651 oldCursor = newCursor;
653 SetCursor(useArrow ? &qd.arrow : cPtr[newCursor]);
664 if (MaxMem(&grow) < pBytes->memWarning)
665 note(noErr, alidNote, "\pWarning: Memory is running low");
667 (void) ResrvMem((Size) FreeMem()); /* move all handles high */
670 /* show memory stats: FreeMem, MaxBlock, PurgeSpace, and StackSpace */
674 unsigned char *pFormat = "\pFree:#k Max:#k Purge:#k Stack:#k";
675 char *pSub = "#"; /* not a pascal string */
676 unsigned char nBuf[16];
682 if (wnEvt.modifiers & shiftKey)
685 if (! (strHnd = NewHandle((Size) 128)))
687 note(noErr, alidNote, "\pOops: Memory stats unavailable!");
691 SetString((StringHandle) strHnd, pFormat);
694 for (i = 1; i <= 4; i++)
696 /* get the replacement number stat */
699 case 1: nStat = FreeMem(); break;
700 case 2: nStat = MaxBlock(); break;
701 case 3: PurgeSpace(&nStat, &contig); break;
702 case 4: nStat = StackSpace(); break;
705 NumToString((nStat >> 10), * (Str255 *) &nBuf);
707 **strHnd += nBuf[0] - 1;
708 nOffset = Munger(strHnd, nOffset, (Ptr) pSub, 1L, (Ptr) &nBuf[1], nBuf[0]);
713 note(noErr, alidNote, (unsigned char *) *strHnd);
714 DisposHandle(strHnd);
718 RecoverMenuEvent(long menuEntry)
720 short menuID = HiWord(menuEntry);
721 short menuItem = LoWord(menuEntry);
729 if (wnEvt.modifiers & optionKey)
733 note(noErr, (alertAppleMenu + menuItem), aboutBuf);
736 default: /* DA's or apple menu items */
738 unsigned char daName[32];
740 GetItem(mHnd[menuApple], menuItem, * (Str255 *) &daName);
741 (void) OpenDeskAcc(daName);
758 WindowPeek frontWindow;
761 if (frontWindow = (WindowPeek) FrontWindow())
762 if ((refNum = frontWindow->windowKind) < 0)
763 CloseDeskAcc(refNum);
776 (void) SystemEdit(menuItem - 1);
786 short wneMask = (in.Front ? everyEvent : (osMask + updateMask));
787 long wneSleep = (in.Front ? 0L : 3L);
794 if (memActivity >= pBytes->memCleanup)
797 (void) WaitNextEvent(wneMask, &wnEvt, wneSleep, (RgnHandle) 0L);
800 (void) IsDialogEvent(&wnEvt);
805 if (((wnEvt.message & osEvtMessageMask) >> 24) == suspendResumeMessage)
807 in.Front = (wnEvt.message & resumeFlag);
808 wneMask = (in.Front ? everyEvent : (osMask + updateMask));
809 wneSleep = (in.Front ? 0L : 3L);
814 /* adjust the FIFO notification queue */
815 if (pNMQ && pNMQ->nmDispose)
817 notifPtr pNMX = pNMQ->nmNext;
819 DisposPtr((Ptr) pNMQ);
831 WindowPtr whichWindow;
833 switch(FindWindow( wnEvt . where , &whichWindow))
836 RecoverMenuEvent(MenuSelect( wnEvt . where ));
840 SystemClick(&wnEvt, whichWindow);
845 Rect boundsRect = qd.screenBits.bounds;
848 InsetRect(&boundsRect, 4, 4);
849 boundsRect.top += GetMBarHeight();
851 DragWindow(whichWindow, * ((Point *) &wnEvt.where), &boundsRect);
853 boundsRect = whichWindow->portRect;
854 offsetPt = * (Point *) &(whichWindow->portBits.bounds);
855 OffsetRect(&boundsRect, -offsetPt.h, -offsetPt.v);
857 * (Rect *) *thermoTHnd = boundsRect;
866 char key = (wnEvt.message & charCodeMask);
868 if (wnEvt.modifiers & cmdKey)
875 note(noErr, alidNote, "\pSorry: Recovery aborted");
879 RecoverMenuEvent(MenuKey(key));
884 /* without windows these events belong to our thermometer */
891 (void) DialogSelect(&wnEvt, &dPtr, &itemHit);
895 if (HiWord(wnEvt.message))
899 (void) DIBadMount(pt, wnEvt.message);
905 } /* switch (wnEvt.what) */
915 /* wait for pending notifications to complete */
917 (void) WaitNextEvent(0, &wnEvt, 3L, (RgnHandle) 0L);
922 /* draw the progress thermometer and frame. 1 level <=> 1 horiz. pixel */
924 drawThermo(WindowPtr wPtr, short inum)
926 itemizeThermo(drawItem);
929 /* manage progress thermometer dialog */
931 itemizeThermo(short itemMode)
937 GetDItem(DLGTHM, uitmThermo, &iTyp, &iHnd, &iRct);
942 SetDItem(DLGTHM, uitmThermo, iTyp, (Handle) drawThermoUPP, &iRct);
952 InsetRect(&iRct, 1, 1);
961 InsetRect(&iRct, 1, 1);
964 iRct.right = iRct.left + in.Recover;
967 iRct.left = iRct.right;
974 /* show only <pid-plname>.0 files in get file dialog */
976 basenameFileFilter(ParmBlkPtr pPB)
980 if (! (pC = (unsigned char *) pPB->fileParam.ioNamePtr))
983 if ((*pC < 4) || (*pC > 28)) /* save/ 1name .0 */
986 if ((pC[*pC - 1] == '.') && (pC[*pC] == '0')) /* bingo! */
995 SFTypeList levlType = {'LEVL'};
998 SFGetFile(sfGetWhere, "\p", basenameFileFilterUPP, 1, levlType,
999 (DlgHookUPP) 0L, &sfGetReply);
1003 if (! sfGetReply.good)
1006 /* get volume (working directory) refnum, basename, and directory i.d. */
1007 vRefNum = sfGetReply.vRefNum;
1008 BlockMove(sfGetReply.fName, lock, sfGetReply.fName[0] + 1);
1010 static CInfoPBRec catInfo;
1012 catInfo.hFileInfo.ioNamePtr = (StringPtr) sfGetReply.fName;
1013 catInfo.hFileInfo.ioVRefNum = sfGetReply.vRefNum;
1014 catInfo.hFileInfo.ioDirID = 0L;
1016 if (PBGetCatInfoSync(&catInfo))
1018 note(noErr, alidNote, "\pSorry: Bad File Info");
1022 dirID = catInfo.hFileInfo.ioFlParID;
1025 /* open the progress thermometer dialog */
1026 (void) GetNewDialog(dlogProgress, (Ptr) &dlgThermo, (WindowPtr) -1L);
1027 if (ResError() || MemError())
1028 note(noErr, alidNote, "\pOops: Progress thermometer unavailable");
1034 itemizeThermo(initItem);
1039 timeCursor = TickCount() + CURS_LATENT;
1040 saveRefNum = gameRefNum = levRefNum = -1;
1049 /* update the thermometer */
1050 if (in.Dialog && ! (in.Recover % 4))
1051 itemizeThermo(invalItem);
1053 if (in.Recover <= MAX_RECOVER_COUNT)
1058 if (saveRezStrings())
1061 note(noErr, alidNote, "\pOK: Recovery succeeded");
1064 /* no messages from here (since we might be quitting) */
1070 oldCursor = curs_Init;
1071 SetCursor(&qd.arrow);
1073 /* clean up abandoned files */
1074 if (gameRefNum >= 0)
1075 (void) FSClose(gameRefNum);
1078 (void) FSClose(levRefNum);
1080 if (saveRefNum >= 0)
1082 (void) FSClose(saveRefNum);
1083 (void) FlushVol((StringPtr) 0L, vRefNum);
1084 /* its corrupted so trash it ... */
1085 (void) HDelete(vRefNum, dirID, savename);
1088 saveRefNum = gameRefNum = levRefNum = -1;
1090 /* close the progress thermometer dialog */
1092 CloseDialog(DLGTHM);
1093 DisposHandle(dlgThermo.items);
1097 /* add friendly, non-essential resource strings to save file */
1102 StringHandle strHnd;
1104 unsigned char *plName;
1106 HCreateResFile(vRefNum, dirID, savename);
1108 sRefNum = HOpenResFile(vRefNum, dirID, savename, fsRdWrPerm);
1111 note(noErr, alidNote, "\pOK: Minor resource map error");
1115 /* savename and hpid get mutilated here... */
1116 plName = savename + 5; /* save/ */
1125 *plName = *savename;
1127 for (i = 1; i <= 2; i++)
1132 rezID = PLAYER_NAME_RES_ID;
1133 strHnd = NewString(* (Str255 *) plName);
1137 rezID = APP_NAME_RES_ID;
1138 strHnd = NewString(* (Str255 *) "\pNetHack");
1144 note(noErr, alidNote, "\pOK: Minor \'STR \' resource error");
1145 CloseResFile(sRefNum);
1149 /* should check for errors... */
1150 AddResource((Handle) strHnd, 'STR ', rezID, * (Str255 *) "\p");
1155 /* should check for errors... */
1156 CloseResFile(sRefNum);
1161 set_levelfile_name(long lev)
1165 /* find the dot. this is guaranteed to happen. */
1166 for (tf = (lock + *lock); *tf != '.'; tf--, lock[0]--)
1169 /* append the level number string (pascal) */
1172 NumToString(lev, * (Str255 *) tf);
1179 note(noErr, alidNote, "\pSorry: File Name Error");
1184 open_levelfile(long lev)
1189 set_levelfile_name(lev);
1193 if ((openErr = HOpen(vRefNum, dirID, lock, fsRdWrPerm, &fRefNum))
1194 && (openErr != fnfErr))
1197 note(noErr, alidNote, "\pSorry: File Open Error");
1201 return (openErr ? -1 : fRefNum);
1205 create_savefile(unsigned char *savename)
1209 /* translate savename to a pascal string (in place) */
1214 for (pC = savename; *pC; pC++);
1216 nameLen = pC - savename;
1218 for ( ; pC > savename; pC--)
1221 *savename = nameLen;
1224 if (HCreate(vRefNum, dirID, savename, MAC_CREATOR, SAVE_TYPE)
1225 || HOpen(vRefNum, dirID, savename, fsRdWrPerm, &fRefNum))
1228 note(noErr, alidNote, "\pSorry: File Create Error");
1236 copy_bytes(short inRefNum, short outRefNum)
1238 char *buf = (char *) pIOBuf;
1239 long bufSiz = pBytes->memIOBuf;
1245 nfrom = read_levelfile(inRefNum, buf, bufSiz);
1249 nto = write_savefile(outRefNum, buf, nfrom);
1256 note(noErr, alidNote, "\pSorry: File Copy Error");
1260 while (nfrom == bufSiz);
1269 struct version_info version_data;
1271 /* level 0 file contains:
1272 * pid of creating process (ignored here)
1273 * level number for current level of save file
1274 * name of save file nethack would have created
1278 lev = in.Recover - 1;
1281 gameRefNum = open_levelfile(0L);
1284 (void) read_levelfile(gameRefNum, (Ptr) &hpid, sizeof(hpid));
1287 saveTemp = read_levelfile(gameRefNum, (Ptr) &savelev, sizeof(savelev));
1289 if (in.Recover && (saveTemp != sizeof(savelev)))
1292 note(noErr, alidNote, "\pSorry: \"checkpoint\" was not enabled");
1297 (void) read_levelfile(gameRefNum, (Ptr) savename, sizeof(savename));
1299 (void) read_levelfile(gameRefNum,
1300 (Ptr) &version_data, sizeof version_data);
1302 /* save file should contain:
1303 * current level (including pets)
1304 * (non-level-based) game state
1308 saveRefNum = create_savefile(savename);
1311 levRefNum = open_levelfile(savelev);
1314 (void) write_savefile(saveRefNum,
1315 (Ptr) &version_data, sizeof version_data);
1317 copy_bytes(levRefNum, saveRefNum);
1320 close_file(&levRefNum);
1326 copy_bytes(gameRefNum, saveRefNum);
1329 close_file(&gameRefNum);
1332 set_levelfile_name(0L);
1337 else if (lev != savelev)
1339 levRefNum = open_levelfile(lev);
1342 /* any or all of these may not exist */
1345 (void) write_savefile(saveRefNum, (Ptr) &levc, sizeof(levc));
1348 copy_bytes(levRefNum, saveRefNum);
1351 close_file(&levRefNum);
1358 if (in.Recover == MAX_RECOVER_COUNT)
1359 close_file(&saveRefNum);
1366 read_levelfile(short rdRefNum, Ptr bufPtr, long count)
1369 long rdCount = count;
1371 if ((rdErr = FSRead(rdRefNum, &rdCount, bufPtr)) && (rdErr != eofErr))
1374 note(noErr, alidNote, "\pSorry: File Read Error");
1382 write_savefile(short wrRefNum, Ptr bufPtr, long count)
1384 long wrCount = count;
1386 if (FSWrite(wrRefNum, &wrCount, bufPtr))
1389 note(noErr, alidNote, "\pSorry: File Write Error");
1397 close_file(short *pFRefNum)
1399 if (FSClose(*pFRefNum) || FlushVol((StringPtr) 0L, vRefNum))
1402 note(noErr, alidNote, "\pSorry: File Close Error");
1410 unlink_file(unsigned char *filename)
1412 if (HDelete(vRefNum, dirID, filename))
1415 note(noErr, alidNote, "\pSorry: File Delete Error");