1 /* aNetHack 0.0.1 mrecover.c $ANH-Date: 1432512798 2015/05/25 00:13:18 $ $ANH-Branch: master $:$ANH-Revision: 1.8 $ */
2 /* Copyright (c) David Hairston, 1993. */
3 /* aNetHack 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 aNetHack to auto-recover crashed games.
40 /**** Toolbox defines ****/
42 /* MPW C headers (99.44% pure) */
45 #include <Resources.h>
54 #include <Notification.h>
57 #include <StandardFile.h>
58 #include <ToolUtils.h>
59 #include <Processes.h>
61 #include <TextUtils.h>
63 #ifdef THINK /* glue for System 7 Icon Family call (needed by Think C 5.0.4) \
65 pascal OSErr
GetIconSuite(Handle
*theIconSuite
, short theResID
,
66 long selector
) = { 0x303C, 0x0501, 0xABC9 };
69 /**** Application defines ****/
72 typedef struct memBytes
/* format of 'memB' resource, preloaded/locked */
75 short memCleanup
; /* 4 - memory monitor activity limit */
76 long memPreempt
; /* 32k - start iff FreeMem() > */
77 long memWarning
; /* 12k - warn if MaxMem() < */
78 long memAbort
; /* 4k - abort if MaxMem() < */
79 long memIOBuf
; /* 16k - read/write buffer size */
80 } memBytes
, *memBytesPtr
, **memBytesHandle
;
82 #define membID 128 /* 'memB' resource ID */
85 #define CURS_FRAME 4L /* 1/15 second - spin cursor */
86 #define CURS_LATENT 60L /* pause before spin cursor */
87 #define curs_Init (-1) /* token for set arrow */
88 #define curs_Total 8 /* maybe 'acur' would be better */
89 #define cursorOffset 128 /* GetCursor(cursorOffset + i) */
94 mbarAppl
, /* normal mode */
95 mbarRecover
, /* in recovery mode */
96 mbarDA
/* DA in front mode */
121 /* standard minimum required Edit menu */
126 alrtNote
, /* general messages */
127 alrtHelp
, /* help message */
130 alertAppleMenu
= 127, /* menuItem to alert ID offset */
135 #define aboutBufSize 80 /* i.e. 2 lines of 320 pixels */
138 #define nmBufSize (32 + aboutBufSize + 32)
139 typedef struct notifRec
{
141 struct notifRec
*nmNext
;
143 unsigned char nmBuf
[nmBufSize
];
144 } notifRec
, *notifPtr
;
146 #define nmPending nmRefCon /* &in.Notify */
147 #define iconNotifyID 128
148 #define ics_1_and_4 0x00000300
151 enum { dlogProgress
= 256 };
152 enum { uitmThermo
= 1 };
153 enum { initItem
, invalItem
, drawItem
};
156 typedef struct modeFlags
{
157 short Front
; /* fg/bg event handling */
158 short Notify
; /* level of pending NM notifications */
159 short Dialog
; /* a modeless dialog is open */
160 short Recover
; /* restoration progress index */
163 /* convenient define to allow easier (for me) parsing of 'vers' resource */
164 typedef struct versXRec
{
167 unsigned char versStr
[]; /* (small string)(large string) */
168 } versXRec
, *versXPtr
, **versXHandle
;
170 /**** Global variables ****/
171 modeFlags in
= { 1 }; /* in Front */
174 unsigned char aboutBuf
[aboutBufSize
]; /* vers 1 "Get Info" string */
175 memBytesPtr pBytes
; /* memory management */
176 unsigned short memActivity
; /* more memory management */
177 MenuHandle mHnd
[menu_Total
];
178 CursPtr cPtr
[curs_Total
]; /* busy cursors */
179 unsigned long timeCursor
; /* next cursor frame time */
180 short oldCursor
= curs_Init
; /* see adjustGUI() below */
181 notifPtr pNMQ
; /* notification queue pointer */
182 notifRec nmt
; /* notification template */
183 DialogTHndl thermoTHnd
;
184 DialogRecord dlgThermo
; /* progress thermometer */
185 #define DLGTHM ((DialogPtr) &dlgThermo)
186 #define WNDTHM ((WindowPtr) &dlgThermo)
187 #define GRFTHM ((GrafPtr) &dlgThermo)
189 Point sfGetWhere
; /* top left corner of get file dialog */
190 Ptr pIOBuf
; /* read/write buffer pointer */
191 short vRefNum
; /* SFGetFile working directory/volume refnum */
192 long dirID
; /* directory i.d. */
193 NMUPP nmCompletionUPP
; /* UPP for nmCompletion */
194 FileFilterUPP basenameFileFilterUPP
; /* UPP for basenameFileFilter */
195 UserItemUPP drawThermoUPP
; /* UPP for progress callback */
197 #define MAX_RECOVER_COUNT 256
199 #define APP_NAME_RES_ID (-16396) /* macfile.h */
200 #define PLAYER_NAME_RES_ID 1001 /* macfile.h */
202 /* variables from util/recover.c */
203 #define SAVESIZE FILENAME
204 unsigned char savename
[SAVESIZE
]; /* originally a C string */
205 unsigned char lock
[256]; /* pascal string */
207 int hpid
; /* aNetHack (unix-style) process i.d. */
208 short saveRefNum
; /* save file descriptor */
209 short gameRefNum
; /* level 0 file descriptor */
210 short levRefNum
; /* level n file descriptor */
212 /**** Prototypes ****/
213 static void warmup(void);
214 static Handle
alignTemplate(ResType
, short, short, short, Point
*);
215 pascal void nmCompletion(NMRec
*);
216 static void noteErrorMessage(unsigned char *, unsigned char *);
217 static void note(short, short, unsigned char *);
218 static void adjustGUI(void);
219 static void adjustMemory(void);
220 static void optionMemStats(void);
221 static void RecoverMenuEvent(long);
222 static void eventLoop(void);
223 static void cooldown(void);
225 pascal void drawThermo(WindowPtr
, short);
226 static void itemizeThermo(short);
227 pascal Boolean
basenameFileFilter(ParmBlkPtr
);
228 static void beginRecover(void);
229 static void continueRecover(void);
230 static void endRecover(void);
231 static short saveRezStrings(void);
233 /* analogous prototypes from util/recover.c */
234 static void set_levelfile_name(long);
235 static short open_levelfile(long);
236 static short create_savefile(unsigned char *);
237 static void copy_bytes(short, short);
238 static void restore_savefile(void);
240 /* auxiliary prototypes */
241 static long read_levelfile(short, Ptr
, long);
242 static long write_savefile(short, Ptr
, long);
243 static void close_file(short *);
244 static void unlink_file(unsigned char *);
255 /* manager initialization */
256 InitGraf(&qd
.thePort
);
263 nmCompletionUPP
= NewNMProc(nmCompletion
);
264 basenameFileFilterUPP
= NewFileFilterProc(basenameFileFilter
);
265 drawThermoUPP
= NewUserItemProc(drawThermo
);
267 /* get system environment, notification requires 6.0 or better */
268 (void) SysEnvirons(curSysEnvVers
, &sysEnv
);
269 if (sysEnv
.systemVersion
< 0x0600) {
270 ParamText("\pAbort: System 6.0 is required", "\p", "\p", "\p");
271 (void) Alert(alidNote
, (ModalFilterUPP
) 0L);
278 /* normally these routines are never reached from here */
289 /* pre-System 7 MultiFinder hack for smooth launch */
290 for (i
= 0; i
< 10; i
++) {
291 if (WaitNextEvent(osMask
, &wnEvt
, 2L, (RgnHandle
) 0L))
292 if (((wnEvt
.message
& osEvtMessageMask
) >> 24)
293 == suspendResumeMessage
)
294 in
.Front
= (wnEvt
.message
& resumeFlag
);
298 /* clear out the Finder info */
300 short message
, count
;
302 CountAppFiles(&message
, &count
);
304 ClrAppFiles(count
--);
308 /* fill out the notification template */
309 nmt
.nmr
.qType
= nmType
;
311 nmt
.nmr
.nmSound
= (Handle
) -1L; /* system beep */
312 nmt
.nmr
.nmStr
= nmt
.nmBuf
;
313 nmt
.nmr
.nmResp
= nmCompletionUPP
;
314 nmt
.nmr
.nmPending
= (long) &in
.Notify
;
318 /* get the app name */
320 ProcessSerialNumber psn
;
322 info
.processInfoLength
= sizeof(info
);
323 info
.processName
= nmt
.nmBuf
;
324 info
.processAppSpec
= NULL
;
325 GetCurrentProcess(&psn
);
326 GetProcessInformation(&psn
, &info
);
329 /* prepend app name (31 chars or less) to notification buffer */
334 GetAppParms(*(Str255
*) &nmt
.nmBuf
, &apRefNum
, &apParams
);
338 /* add formatting (two line returns) */
339 nmt
.nmBuf
[++(nmt
.nmBuf
[0])] = '\r';
340 nmt
.nmBuf
[++(nmt
.nmBuf
[0])] = '\r';
342 /**** note() is usable now but not aesthetically complete ****/
344 /* get notification icon */
345 if (sysEnv
.systemVersion
< 0x0700) {
346 if (!(nmt
.nmr
.nmIcon
= GetResource('SICN', iconNotifyID
)))
347 note(nilHandleErr
, 0, "\pNil SICN Handle");
349 if (GetIconSuite(&nmt
.nmr
.nmIcon
, iconNotifyID
, ics_1_and_4
))
350 note(nilHandleErr
, 0, "\pBad Icon Family");
353 /* load and align various dialog/alert templates */
354 (void) alignTemplate('ALRT', alidNote
, 0, 4, (Point
*) 0L);
355 (void) alignTemplate('ALRT', alidHelp
, 0, 4, (Point
*) 0L);
357 thermoTHnd
= (DialogTHndl
) alignTemplate('DLOG', dlogProgress
, 20, 8,
360 (void) alignTemplate('DLOG', getDlgID
, 0, 6, (Point
*) &sfGetWhere
);
362 /* get the "busy cursors" (preloaded/locked) */
363 for (i
= 0; i
< curs_Total
; i
++) {
366 if (!(cHnd
= GetCursor(i
+ cursorOffset
)))
367 note(nilHandleErr
, 0, "\pNil CURS Handle");
372 /* get the 'vers' 1 long (Get Info) string - About Recover... */
376 if (!(vHnd
= (versXHandle
) GetResource('vers', 1)))
377 note(nilHandleErr
, 0, "\pNil vers Handle");
379 i
= (**vHnd
).versStr
[0] + 1; /* offset to Get Info pascal string */
381 if ((aboutBuf
[0] = (**vHnd
).versStr
[i
]) > (aboutBufSize
- 1))
382 aboutBuf
[0] = aboutBufSize
- 1;
386 MoveHHi((Handle
) vHnd
); /* DEE - Fense ... */
387 HLock((Handle
) vHnd
);
388 BlockMove(&((**vHnd
).versStr
[i
]), &(aboutBuf
[1]), aboutBuf
[0]);
389 ReleaseResource((Handle
) vHnd
);
392 /* form the menubar */
393 for (i
= 0; i
< menu_Total
; i
++) {
394 if (!(mHnd
[i
] = GetMenu(i
+ muidApple
)))
395 note(nilHandleErr
, 0, "\pNil MENU Handle");
397 /* expand the apple menu */
399 AddResMenu(mHnd
[menuApple
], 'DRVR');
401 InsertMenu(mHnd
[i
], 0);
404 /* pre-emptive memory check */
406 memBytesHandle hBytes
;
409 if (!(hBytes
= (memBytesHandle
) GetResource('memB', membID
)))
410 note(nilHandleErr
, 0, "\pNil Memory Handle");
414 if (MaxMem(&grow
) < pBytes
->memPreempt
)
415 note(memFullErr
, 0, "\pMore Memory Required\rTry adding 16k");
417 memActivity
= pBytes
->memCleanup
; /* force initial cleanup */
420 /* get the I/O buffer */
421 if (!(pIOBuf
= NewPtr(pBytes
->memIOBuf
)))
422 note(memFullErr
, 0, "\pNil I/O Pointer");
425 /* align a window-related template to the main screen */
427 alignTemplate(ResType rezType
, short rezID
, short vOff
, short vDenom
,
433 vOff
+= GetMBarHeight();
435 if (!(rtnHnd
= GetResource(rezType
, rezID
)))
436 note(nilHandleErr
, 0, "\pNil Template Handle");
438 pRct
= (Rect
*) *rtnHnd
;
440 /* don't move memory while aligning rect */
441 pRct
->right
-= pRct
->left
; /* width */
442 pRct
->bottom
-= pRct
->top
; /* height */
443 pRct
->left
= (qd
.screenBits
.bounds
.right
- pRct
->right
) / 2;
444 pRct
->top
= (qd
.screenBits
.bounds
.bottom
- pRct
->bottom
- vOff
) / vDenom
;
446 pRct
->right
+= pRct
->left
;
447 pRct
->bottom
+= pRct
->top
;
450 *pPt
= *(Point
*) pRct
; /* top left corner */
455 /* notification completion routine */
457 nmCompletion(NMRec
*pNMR
)
459 (void) NMRemove(pNMR
);
461 (*(short *) (pNMR
->nmPending
))--; /* decrement pending note level */
462 ((notifPtr
) pNMR
)->nmDispose
= 1; /* allow DisposPtr() */
466 * handle errors inside of note(). the error message is appended to the
467 * given message but on a separate line and must fit within nmBufSize.
470 noteErrorMessage(unsigned char *msg
, unsigned char *errMsg
)
472 short i
= nmt
.nmBuf
[0] + 1; /* insertion point */
474 BlockMove(&msg
[1], &nmt
.nmBuf
[i
], msg
[0]);
475 nmt
.nmBuf
[i
+ msg
[0]] = '\r';
476 nmt
.nmBuf
[0] += (msg
[0] + 1);
478 note(memFullErr
, 0, errMsg
);
482 * display messages using Notification Manager or an alert.
483 * no run-length checking is done. the messages are created to fit
484 * in the allocated space (nmBufSize and aboutBufSize).
487 note(short errorSignal
, short alertID
, unsigned char *msg
)
492 if (MaxMem(&grow
) < pBytes
->memAbort
)
493 noteErrorMessage(msg
, "\pOut of Memory");
496 if (errorSignal
|| !in
.Front
) {
498 short i
= nmt
.nmBuf
[0] + 1; /* insertion point */
500 if (errorSignal
) /* use notification template */
504 /* we're going to abort so add in this prefix */
505 BlockMove("Abort: ", &nmt
.nmBuf
[i
], 7);
508 } else /* allocate a notification record */
510 if (!(pNMR
= (notifPtr
) NewPtr(sizeof(notifRec
))))
511 noteErrorMessage(msg
, "\pNil New Pointer");
515 pNMR
->nmr
.nmStr
= (StringPtr
) & (pNMR
->nmBuf
);
517 /* update the notification queue */
523 /* find the end of the queue */
524 for (pNMX
= pNMQ
; pNMX
->nmNext
; pNMX
= pNMX
->nmNext
)
531 /* concatenate the message */
532 BlockMove(&msg
[1], &((pNMR
->nmBuf
)[i
]), msg
[0]);
533 (pNMR
->nmBuf
)[0] += msg
[0];
535 in
.Notify
++; /* increase note pending level */
537 NMInstall((NMRec
*) pNMR
);
545 /* in front and no error so use an alert */
546 ParamText(msg
, "\p", "\p", "\p");
547 (void) Alert(alertID
, (ModalFilterUPP
) 0L);
556 static short oldMenubar
= mbar_Init
; /* force initial update */
558 WindowPeek frontWindow
;
560 /* oldCursor is external so it can be reset in endRecover() */
561 static short newCursor
= curs_Init
;
562 unsigned long timeNow
;
565 /* adjust menubar 1st */
566 newMenubar
= in
.Recover
? mbarRecover
: mbarAppl
;
568 /* desk accessories take precedence */
569 if (frontWindow
= (WindowPeek
) FrontWindow())
570 if (frontWindow
->windowKind
< 0)
573 if (newMenubar
!= oldMenubar
) {
575 switch (oldMenubar
= newMenubar
) {
577 EnableItem(mHnd
[menuFile
], mitmOpen
);
578 SetItemMark(mHnd
[menuFile
], mitmOpen
, noMark
);
579 DisableItem(mHnd
[menuFile
], mitmClose_DA
);
580 DisableItem(mHnd
[menuEdit
], 0);
584 DisableItem(mHnd
[menuFile
], mitmOpen
);
585 SetItemMark(mHnd
[menuFile
], mitmOpen
, checkMark
);
586 DisableItem(mHnd
[menuFile
], mitmClose_DA
);
587 DisableItem(mHnd
[menuEdit
], 0);
591 DisableItem(mHnd
[menuFile
], mitmOpen
);
592 EnableItem(mHnd
[menuFile
], mitmClose_DA
);
593 EnableItem(mHnd
[menuEdit
], 0);
600 /* now adjust the cursor */
601 if (useArrow
= (!in
.Recover
|| (newMenubar
== mbarDA
)))
602 newCursor
= curs_Init
;
603 else if ((timeNow
= TickCount()) >= timeCursor
) /* spin cursor */
605 timeCursor
= timeNow
+ CURS_FRAME
;
606 if (++newCursor
>= curs_Total
)
610 if (newCursor
!= oldCursor
) {
611 oldCursor
= newCursor
;
613 SetCursor(useArrow
? &qd
.arrow
: cPtr
[newCursor
]);
624 if (MaxMem(&grow
) < pBytes
->memWarning
)
625 note(noErr
, alidNote
, "\pWarning: Memory is running low");
627 (void) ResrvMem((Size
) FreeMem()); /* move all handles high */
630 /* show memory stats: FreeMem, MaxBlock, PurgeSpace, and StackSpace */
634 unsigned char *pFormat
= "\pFree:#k Max:#k Purge:#k Stack:#k";
635 char *pSub
= "#"; /* not a pascal string */
636 unsigned char nBuf
[16];
642 if (wnEvt
.modifiers
& shiftKey
)
645 if (!(strHnd
= NewHandle((Size
) 128))) {
646 note(noErr
, alidNote
, "\pOops: Memory stats unavailable!");
650 SetString((StringHandle
) strHnd
, pFormat
);
653 for (i
= 1; i
<= 4; i
++) {
654 /* get the replacement number stat */
663 PurgeSpace(&nStat
, &contig
);
666 nStat
= StackSpace();
670 NumToString((nStat
>> 10), *(Str255
*) &nBuf
);
672 **strHnd
+= nBuf
[0] - 1;
674 Munger(strHnd
, nOffset
, (Ptr
) pSub
, 1L, (Ptr
) &nBuf
[1], nBuf
[0]);
679 note(noErr
, alidNote
, (unsigned char *) *strHnd
);
680 DisposHandle(strHnd
);
684 RecoverMenuEvent(long menuEntry
)
686 short menuID
= HiWord(menuEntry
);
687 short menuItem
= LoWord(menuEntry
);
693 if (wnEvt
.modifiers
& optionKey
)
697 note(noErr
, (alertAppleMenu
+ menuItem
), aboutBuf
);
700 default: /* DA's or apple menu items */
702 unsigned char daName
[32];
704 GetItem(mHnd
[menuApple
], menuItem
, *(Str255
*) &daName
);
705 (void) OpenDeskAcc(daName
);
719 WindowPeek frontWindow
;
722 if (frontWindow
= (WindowPeek
) FrontWindow())
723 if ((refNum
= frontWindow
->windowKind
) < 0)
724 CloseDeskAcc(refNum
);
736 (void) SystemEdit(menuItem
- 1);
746 short wneMask
= (in
.Front
? everyEvent
: (osMask
+ updateMask
));
747 long wneSleep
= (in
.Front
? 0L : 3L);
753 if (memActivity
>= pBytes
->memCleanup
)
756 (void) WaitNextEvent(wneMask
, &wnEvt
, wneSleep
, (RgnHandle
) 0L);
759 (void) IsDialogEvent(&wnEvt
);
761 switch (wnEvt
.what
) {
763 if (((wnEvt
.message
& osEvtMessageMask
) >> 24)
764 == suspendResumeMessage
) {
765 in
.Front
= (wnEvt
.message
& resumeFlag
);
766 wneMask
= (in
.Front
? everyEvent
: (osMask
+ updateMask
));
767 wneSleep
= (in
.Front
? 0L : 3L);
772 /* adjust the FIFO notification queue */
773 if (pNMQ
&& pNMQ
->nmDispose
) {
774 notifPtr pNMX
= pNMQ
->nmNext
;
776 DisposPtr((Ptr
) pNMQ
);
787 WindowPtr whichWindow
;
789 switch (FindWindow(wnEvt
.where
, &whichWindow
)) {
791 RecoverMenuEvent(MenuSelect(wnEvt
.where
));
795 SystemClick(&wnEvt
, whichWindow
);
799 Rect boundsRect
= qd
.screenBits
.bounds
;
802 InsetRect(&boundsRect
, 4, 4);
803 boundsRect
.top
+= GetMBarHeight();
805 DragWindow(whichWindow
, *((Point
*) &wnEvt
.where
),
808 boundsRect
= whichWindow
->portRect
;
809 offsetPt
= *(Point
*) &(whichWindow
->portBits
.bounds
);
810 OffsetRect(&boundsRect
, -offsetPt
.h
, -offsetPt
.v
);
812 *(Rect
*) *thermoTHnd
= boundsRect
;
818 char key
= (wnEvt
.message
& charCodeMask
);
820 if (wnEvt
.modifiers
& cmdKey
) {
824 note(noErr
, alidNote
, "\pSorry: Recovery aborted");
827 RecoverMenuEvent(MenuKey(key
));
831 /* without windows these events belong to our thermometer */
837 (void) DialogSelect(&wnEvt
, &dPtr
, &itemHit
);
841 if (HiWord(wnEvt
.message
)) {
842 Point pt
= { 60, 60 };
844 (void) DIBadMount(pt
, wnEvt
.message
);
850 } /* switch (wnEvt.what) */
860 /* wait for pending notifications to complete */
862 (void) WaitNextEvent(0, &wnEvt
, 3L, (RgnHandle
) 0L);
867 /* draw the progress thermometer and frame. 1 level <=> 1 horiz. pixel */
869 drawThermo(WindowPtr wPtr
, short inum
)
871 itemizeThermo(drawItem
);
874 /* manage progress thermometer dialog */
876 itemizeThermo(short itemMode
)
882 GetDItem(DLGTHM
, uitmThermo
, &iTyp
, &iHnd
, &iRct
);
886 SetDItem(DLGTHM
, uitmThermo
, iTyp
, (Handle
) drawThermoUPP
, &iRct
);
895 InsetRect(&iRct
, 1, 1);
903 InsetRect(&iRct
, 1, 1);
906 iRct
.right
= iRct
.left
+ in
.Recover
;
909 iRct
.left
= iRct
.right
;
916 /* show only <pid-plname>.0 files in get file dialog */
918 basenameFileFilter(ParmBlkPtr pPB
)
922 if (!(pC
= (unsigned char *) pPB
->fileParam
.ioNamePtr
))
925 if ((*pC
< 4) || (*pC
> 28)) /* save/ 1name .0 */
928 if ((pC
[*pC
- 1] == '.') && (pC
[*pC
] == '0')) /* bingo! */
937 SFTypeList levlType
= { 'LEVL' };
940 SFGetFile(sfGetWhere
, "\p", basenameFileFilterUPP
, 1, levlType
,
941 (DlgHookUPP
) 0L, &sfGetReply
);
945 if (!sfGetReply
.good
)
948 /* get volume (working directory) refnum, basename, and directory i.d. */
949 vRefNum
= sfGetReply
.vRefNum
;
950 BlockMove(sfGetReply
.fName
, lock
, sfGetReply
.fName
[0] + 1);
952 static CInfoPBRec catInfo
;
954 catInfo
.hFileInfo
.ioNamePtr
= (StringPtr
) sfGetReply
.fName
;
955 catInfo
.hFileInfo
.ioVRefNum
= sfGetReply
.vRefNum
;
956 catInfo
.hFileInfo
.ioDirID
= 0L;
958 if (PBGetCatInfoSync(&catInfo
)) {
959 note(noErr
, alidNote
, "\pSorry: Bad File Info");
963 dirID
= catInfo
.hFileInfo
.ioFlParID
;
966 /* open the progress thermometer dialog */
967 (void) GetNewDialog(dlogProgress
, (Ptr
) &dlgThermo
, (WindowPtr
) -1L);
968 if (ResError() || MemError())
969 note(noErr
, alidNote
, "\pOops: Progress thermometer unavailable");
974 itemizeThermo(initItem
);
979 timeCursor
= TickCount() + CURS_LATENT
;
980 saveRefNum
= gameRefNum
= levRefNum
= -1;
989 /* update the thermometer */
990 if (in
.Dialog
&& !(in
.Recover
% 4))
991 itemizeThermo(invalItem
);
993 if (in
.Recover
<= MAX_RECOVER_COUNT
)
998 if (saveRezStrings())
1001 note(noErr
, alidNote
, "\pOK: Recovery succeeded");
1004 /* no messages from here (since we might be quitting) */
1010 oldCursor
= curs_Init
;
1011 SetCursor(&qd
.arrow
);
1013 /* clean up abandoned files */
1014 if (gameRefNum
>= 0)
1015 (void) FSClose(gameRefNum
);
1018 (void) FSClose(levRefNum
);
1020 if (saveRefNum
>= 0) {
1021 (void) FSClose(saveRefNum
);
1022 (void) FlushVol((StringPtr
) 0L, vRefNum
);
1023 /* its corrupted so trash it ... */
1024 (void) HDelete(vRefNum
, dirID
, savename
);
1027 saveRefNum
= gameRefNum
= levRefNum
= -1;
1029 /* close the progress thermometer dialog */
1031 CloseDialog(DLGTHM
);
1032 DisposHandle(dlgThermo
.items
);
1036 /* add friendly, non-essential resource strings to save file */
1041 StringHandle strHnd
;
1043 unsigned char *plName
;
1045 HCreateResFile(vRefNum
, dirID
, savename
);
1047 sRefNum
= HOpenResFile(vRefNum
, dirID
, savename
, fsRdWrPerm
);
1049 note(noErr
, alidNote
, "\pOK: Minor resource map error");
1053 /* savename and hpid get mutilated here... */
1054 plName
= savename
+ 5; /* save/ */
1061 *plName
= *savename
;
1063 for (i
= 1; i
<= 2; i
++) {
1066 rezID
= PLAYER_NAME_RES_ID
;
1067 strHnd
= NewString(*(Str255
*) plName
);
1071 rezID
= APP_NAME_RES_ID
;
1072 strHnd
= NewString(*(Str255
*) "\paNetHack");
1077 note(noErr
, alidNote
, "\pOK: Minor \'STR \' resource error");
1078 CloseResFile(sRefNum
);
1082 /* should check for errors... */
1083 AddResource((Handle
) strHnd
, 'STR ', rezID
, *(Str255
*) "\p");
1088 /* should check for errors... */
1089 CloseResFile(sRefNum
);
1094 set_levelfile_name(long lev
)
1098 /* find the dot. this is guaranteed to happen. */
1099 for (tf
= (lock
+ *lock
); *tf
!= '.'; tf
--, lock
[0]--)
1102 /* append the level number string (pascal) */
1104 NumToString(lev
, *(Str255
*) tf
);
1110 note(noErr
, alidNote
, "\pSorry: File Name Error");
1115 open_levelfile(long lev
)
1120 set_levelfile_name(lev
);
1124 if ((openErr
= HOpen(vRefNum
, dirID
, lock
, fsRdWrPerm
, &fRefNum
))
1125 && (openErr
!= fnfErr
)) {
1127 note(noErr
, alidNote
, "\pSorry: File Open Error");
1131 return (openErr
? -1 : fRefNum
);
1135 create_savefile(unsigned char *savename
)
1139 /* translate savename to a pascal string (in place) */
1144 for (pC
= savename
; *pC
; pC
++)
1147 nameLen
= pC
- savename
;
1149 for (; pC
> savename
; pC
--)
1152 *savename
= nameLen
;
1155 if (HCreate(vRefNum
, dirID
, savename
, MAC_CREATOR
, SAVE_TYPE
)
1156 || HOpen(vRefNum
, dirID
, savename
, fsRdWrPerm
, &fRefNum
)) {
1158 note(noErr
, alidNote
, "\pSorry: File Create Error");
1166 copy_bytes(short inRefNum
, short outRefNum
)
1168 char *buf
= (char *) pIOBuf
;
1169 long bufSiz
= pBytes
->memIOBuf
;
1174 nfrom
= read_levelfile(inRefNum
, buf
, bufSiz
);
1178 nto
= write_savefile(outRefNum
, buf
, nfrom
);
1184 note(noErr
, alidNote
, "\pSorry: File Copy Error");
1187 } while (nfrom
== bufSiz
);
1196 struct version_info version_data
;
1198 /* level 0 file contains:
1199 * pid of creating process (ignored here)
1200 * level number for current level of save file
1201 * name of save file anethack would have created
1205 lev
= in
.Recover
- 1;
1207 gameRefNum
= open_levelfile(0L);
1210 (void) read_levelfile(gameRefNum
, (Ptr
) &hpid
, sizeof(hpid
));
1214 read_levelfile(gameRefNum
, (Ptr
) &savelev
, sizeof(savelev
));
1216 if (in
.Recover
&& (saveTemp
!= sizeof(savelev
))) {
1218 note(noErr
, alidNote
, "\pSorry: \"checkpoint\" was not enabled");
1223 (void) read_levelfile(gameRefNum
, (Ptr
) savename
,
1226 (void) read_levelfile(gameRefNum
, (Ptr
) &version_data
,
1227 sizeof version_data
);
1229 /* save file should contain:
1230 * current level (including pets)
1231 * (non-level-based) game state
1235 saveRefNum
= create_savefile(savename
);
1238 levRefNum
= open_levelfile(savelev
);
1241 (void) write_savefile(saveRefNum
, (Ptr
) &version_data
,
1242 sizeof version_data
);
1244 copy_bytes(levRefNum
, saveRefNum
);
1247 close_file(&levRefNum
);
1253 copy_bytes(gameRefNum
, saveRefNum
);
1256 close_file(&gameRefNum
);
1259 set_levelfile_name(0L);
1263 } else if (lev
!= savelev
) {
1264 levRefNum
= open_levelfile(lev
);
1265 if (levRefNum
>= 0) {
1266 /* any or all of these may not exist */
1269 (void) write_savefile(saveRefNum
, (Ptr
) &levc
, sizeof(levc
));
1272 copy_bytes(levRefNum
, saveRefNum
);
1275 close_file(&levRefNum
);
1282 if (in
.Recover
== MAX_RECOVER_COUNT
)
1283 close_file(&saveRefNum
);
1290 read_levelfile(short rdRefNum
, Ptr bufPtr
, long count
)
1293 long rdCount
= count
;
1295 if ((rdErr
= FSRead(rdRefNum
, &rdCount
, bufPtr
)) && (rdErr
!= eofErr
)) {
1297 note(noErr
, alidNote
, "\pSorry: File Read Error");
1305 write_savefile(short wrRefNum
, Ptr bufPtr
, long count
)
1307 long wrCount
= count
;
1309 if (FSWrite(wrRefNum
, &wrCount
, bufPtr
)) {
1311 note(noErr
, alidNote
, "\pSorry: File Write Error");
1319 close_file(short *pFRefNum
)
1321 if (FSClose(*pFRefNum
) || FlushVol((StringPtr
) 0L, vRefNum
)) {
1323 note(noErr
, alidNote
, "\pSorry: File Close Error");
1331 unlink_file(unsigned char *filename
)
1333 if (HDelete(vRefNum
, dirID
, filename
)) {
1335 note(noErr
, alidNote
, "\pSorry: File Delete Error");