Blindfold removal fix
[slashemextended.git] / src / save.c
blob0aa8b7da4024a1bb76780c04fe1a4885f885bafa
1 /* SCCS Id: @(#)save.c 3.4 2003/11/14 */
2 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
3 /* NetHack may be freely redistributed. See license for details. */
5 #include "hack.h"
6 #include "lev.h"
7 #include "quest.h"
9 #ifndef NO_SIGNAL
10 #include <signal.h>
11 #endif
12 #if !defined(LSC) && !defined(O_WRONLY) && !defined(AZTEC_C)
13 #include <fcntl.h>
14 #endif
16 #ifdef MFLOPPY
17 long bytes_counted;
18 static int count_only;
19 #endif
21 /*WAC boolean here to keep track of quit status*/
22 boolean saverestore;
24 #ifdef MICRO
25 int dotcnt, dotrow; /* also used in restore */
26 #endif
28 #ifdef ZEROCOMP
29 STATIC_DCL void bputc(int);
30 #endif
31 STATIC_DCL void savelevchn(int,int);
32 STATIC_DCL void savedamage(int,int);
33 STATIC_DCL void saveobjchn(int,struct obj *,int);
34 STATIC_DCL void savemonchn(int,struct monst *,int);
35 STATIC_DCL void savetrapchn(int,struct trap *,int);
36 STATIC_DCL void savegamestate(int,int);
37 #ifdef MFLOPPY
38 STATIC_DCL void savelev0(int,XCHAR_P,int);
39 STATIC_DCL boolean swapout_oldest(void);
40 STATIC_DCL void copyfile(char *,char *);
41 #endif /* MFLOPPY */
42 #ifdef GCC_WARN
43 static long nulls[10];
44 #else
45 #define nulls nul
46 #endif
48 #if defined(UNIX) || defined(VMS) || defined(__EMX__) || defined(WIN32)
49 #define HUP if (!program_state.done_hup)
50 #else
51 #define HUP
52 #endif
54 #if defined(STATUS_COLORS) && defined(TEXTCOLOR)
55 extern const struct percent_color_option *hp_colors;
56 extern const struct percent_color_option *pw_colors;
57 extern const struct text_color_option *text_colors;
58 #endif
60 #ifdef MENU_COLOR
61 extern struct menucoloring *menu_colorings;
62 #endif
64 /* need to preserve these during save to avoid accessing freed memory */
65 static unsigned ustuck_id = 0, usteed_id = 0;
67 int
68 dosave()
70 #ifdef KEEP_SAVE
71 /*WAC for reloading*/
72 register int fd;
73 #endif
75 if (iflags.debug_fuzzer) return 0;
77 clear_nhwindow(WIN_MESSAGE);
78 if(yn("Really save?") == 'n') {
79 clear_nhwindow(WIN_MESSAGE);
80 if(multi > 0) nomul(0, 0, FALSE);
81 } else {
82 clear_nhwindow(WIN_MESSAGE);
83 pline("Saving...");
84 u.cnd_saveamount++;
85 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
86 program_state.done_hup = 0;
87 #endif
88 #ifdef KEEP_SAVE
89 saverestore = FALSE;
90 if (flags.keep_savefile)
91 if(yn("Really quit?") == 'n') saverestore = TRUE;
92 if(dosave0() && !saverestore) {
93 #else
94 if(dosave0()) {
95 #endif
96 program_state.something_worth_saving = 0;
97 u.uhp = -1; /* universal game's over indicator */
98 /* make sure they see the Saving message */
99 display_nhwindow(WIN_MESSAGE, TRUE);
100 exit_nhwindows("Be seeing you...");
101 terminate(EXIT_SUCCESS);
103 /*WAC redraw later
104 else (void)doredraw();*/
106 #ifdef KEEP_SAVE
107 if (saverestore) {
108 /*WAC pulled this from pcmain.c - restore game from the file just saved*/
109 fd = create_levelfile(0);
110 if (fd < 0) {
111 raw_print("Cannot create lock file");
112 } else {
113 hackpid = 1;
114 write(fd, (void *) &hackpid, sizeof(hackpid));
115 close(fd);
117 #ifdef MFLOPPY
118 level_info[0].where = ACTIVE;
119 #endif
121 fd = restore_saved_game();
122 if (fd >= 0) dorecover(fd);
123 check_special_room(FALSE);
124 flags.move = 0;
125 /*WAC correct these after restore*/
126 if(flags.moonphase == FULL_MOON)
127 change_luck(1);
128 if(flags.moonphase == NEW_MOON)
129 u.ualign.record += 3;
130 if (u.ualign.record > u.alignlim) u.ualign.record = u.alignlim;
131 if(flags.friday13) {
132 change_luck(-1);
133 u.ualign.record += 10;
134 if (u.ualign.record > u.alignlim) u.ualign.record = u.alignlim;
136 if(iflags.window_inited)
137 clear_nhwindow(WIN_MESSAGE);
139 saverestore = FALSE;
140 #endif
141 (void)doredraw();
142 return 0;
147 /* returns 1 if save successful */
149 dosave0()
151 const char *fq_save;
152 register int fd, ofd;
153 xchar ltmp;
154 d_level uz_save;
155 char whynot[BUFSZ];
157 if (!SAVEF[0])
158 return 0;
159 fq_save = fqname(SAVEF, SAVEPREFIX, 1); /* level files take 0 */
161 #ifndef NO_SIGNAL
162 #if defined(UNIX) || defined(VMS)
163 sethanguphandler((void (*)(int) ) SIG_IGN);
164 #endif
165 (void) signal(SIGINT, SIG_IGN);
166 #endif
168 #if defined(MICRO) && defined(MFLOPPY)
169 if (!saveDiskPrompt(0)) return 0;
170 #endif
172 HUP if (iflags.window_inited) {
173 uncompress_area(fq_save, SAVEF);
174 fd = open_savefile();
175 if (fd > 0) {
176 (void) close(fd);
177 clear_nhwindow(WIN_MESSAGE);
178 There("seems to be an old save file.");
179 if (yn("Overwrite the old file?") == 'n') {
180 compress_area(fq_save, SAVEF);
181 #ifdef KEEP_SAVE
182 /*WAC don't restore if you didn't save*/
183 saverestore = FALSE;
184 #endif
185 return 0;
190 HUP mark_synch(); /* flush any buffered screen output */
192 fd = create_savefile();
193 if(fd < 0) {
194 HUP pline("Cannot open save file.");
195 (void) delete_savefile(); /* ab@unido */
196 return(0);
199 #ifdef WHEREIS_FILE
200 touch_whereis();
201 #endif
203 vision_recalc(2); /* shut down vision to prevent problems
204 in the event of an impossible() call */
206 /* undo date-dependent luck adjustments made at startup time */
207 if(flags.moonphase == FULL_MOON) /* ut-sally!fletcher */
208 change_luck(-1); /* and unido!ab */
209 if(flags.moonphase == NEW_MOON) /* ut-sally!fletcher */
210 u.ualign.record += 3;
211 if (u.ualign.record > u.alignlim) u.ualign.record = u.alignlim;
212 if(flags.friday13) {
213 change_luck(1);
214 u.ualign.record += 10;
215 if (u.ualign.record > u.alignlim) u.ualign.record = u.alignlim;
217 if(iflags.window_inited)
218 HUP clear_nhwindow(WIN_MESSAGE);
220 #if defined(MICRO) && defined(TTY_GRAPHICS)
221 if (!strncmpi("tty", windowprocs.name, 3)) {
222 dotcnt = 0;
223 dotrow = 2;
224 curs(WIN_MAP, 1, 1);
225 putstr(WIN_MAP, 0, "Saving:");
227 #endif
228 #ifdef MFLOPPY
229 /* make sure there is enough disk space */
230 if (iflags.checkspace) {
231 long fds, needed;
233 savelev(fd, ledger_no(&u.uz), COUNT_SAVE);
234 savegamestate(fd, COUNT_SAVE);
235 needed = bytes_counted;
237 for (ltmp = 1; ltmp <= maxledgerno(); ltmp++)
238 if (ltmp != ledger_no(&u.uz) && level_info[ltmp].where)
239 needed += level_info[ltmp].size + (sizeof ltmp);
240 fds = freediskspace(fq_save);
241 if (needed > fds) {
242 HUP {
243 There("is insufficient space on SAVE disk.");
244 pline("Require %ld bytes but only have %ld.", needed, fds);
246 flushout();
247 (void) close(fd);
248 (void) delete_savefile();
249 return 0;
252 co_false();
254 #endif /* MFLOPPY */
256 if (u.hangupcheat < 2) u.hangupcheat = 0;
258 store_version(fd);
259 #ifdef STORE_PLNAME_IN_FILE
260 bwrite(fd, (void *) plname, PL_NSIZ);
261 bwrite(fd, (void *) plalias, PL_NSIZ);
262 #endif
263 ustuck_id = (u.ustuck ? u.ustuck->m_id : 0);
264 usteed_id = (u.usteed ? u.usteed->m_id : 0);
266 savelev(fd, ledger_no(&u.uz), WRITE_SAVE | FREE_SAVE);
267 /*Keep things from beeing freed if not restoring*/
269 #ifdef KEEP_SAVE
270 if (saverestore) savegamestate(fd, WRITE_SAVE);
271 else
272 #endif
274 savegamestate(fd, WRITE_SAVE | FREE_SAVE);
276 /* While copying level files around, zero out u.uz to keep
277 * parts of the restore code from completely initializing all
278 * in-core data structures, since all we're doing is copying.
279 * This also avoids at least one nasty core dump.
281 uz_save = u.uz;
282 u.uz.dnum = u.uz.dlevel = 0;
283 /* these pointers are no longer valid, and at least u.usteed
284 * may mislead place_monster() on other levels
286 setustuck((struct monst *)0);
287 u.usteed = (struct monst *)0;
289 for(ltmp = (xchar)1; ltmp <= maxledgerno(); ltmp++) {
290 if (ltmp == ledger_no(&uz_save)) continue;
291 if (!(level_info[ltmp].flags & LFILE_EXISTS)) continue;
292 #if defined(MICRO) && defined(TTY_GRAPHICS)
293 curs(WIN_MAP, 1 + dotcnt++, dotrow);
294 if (dotcnt >= (COLNO - 1)) {
295 dotrow++;
296 dotcnt = 0;
298 putstr(WIN_MAP, 0, ".");
299 mark_synch();
300 #endif
301 ofd = open_levelfile(ltmp, whynot);
302 if (ofd < 0) {
303 HUP pline("%s", whynot);
304 (void) close(fd);
305 /*(void) delete_savefile();
306 HUP killer = whynot;
307 HUP done(TRICKED);*/
308 pline("Something is wrong here. This would have been a trickery, but I'm not that cruel. --Amy");
309 return(0);
311 minit(); /* ZEROCOMP */
312 getlev(ofd, hackpid, ltmp, FALSE);
313 (void) close(ofd);
314 bwrite(fd, (void *) &ltmp, sizeof ltmp); /* level number*/
315 savelev(fd, ltmp, WRITE_SAVE | FREE_SAVE); /* actual level*/
316 delete_levelfile(ltmp);
318 bclose(fd);
320 u.uz = uz_save;
322 /* get rid of current level --jgm */
324 delete_levelfile(ledger_no(&u.uz));
325 delete_levelfile(0);
326 #ifdef WHEREIS_FILE
327 delete_whereis();
328 #endif
329 compress_area(FILE_AREA_SAVE, fq_save);
330 return(1);
333 #ifndef HANGUPHANDLING
335 #if defined(UNIX) || defined(VMS) || defined (__EMX__) || defined(WIN32)
336 /*ARGSUSED*/
337 void
338 hangup(sig_unused) /* called as signal() handler, so sent at least one arg */
339 int sig_unused;
341 # ifdef NOSAVEONHANGUP
342 (void) signal(SIGINT, SIG_IGN);
343 clearlocks();
344 # ifndef VMS
345 terminate(EXIT_FAILURE);
346 # endif
347 # else /* SAVEONHANGUP */
348 if (!program_state.done_hup++) {
349 if (program_state.something_worth_saving) {
351 if (u.hangupcheat) {
352 u.hangupcheat++;
353 u.hanguppenalty += 10; /* unfortunately we can't determine if you hanged up during a prompt! --Amy */
354 if (multi) u.hangupparalysis += abs(multi);
355 if (u.hangupparalysis > 5) u.hangupparalysis = 5; /* sanity check */
358 (void) dosave0();
361 # ifdef VMS
362 /* don't call exit when already within an exit handler;
363 that would cancel any other pending user-mode handlers */
364 if (!program_state.exiting)
365 # endif
367 clearlocks();
368 terminate(EXIT_FAILURE);
371 # endif
372 return;
374 #endif
376 #endif /* HANGUPHANDLING */
378 STATIC_OVL void
379 savegamestate(fd, mode)
380 register int fd, mode;
382 int uid;
383 struct obj * bc_objs = (struct obj *)0;
384 #if defined(RECORD_REALTIME) || defined(REALTIME_ON_BOTL)
385 time_t realtime;
386 #endif
388 #ifdef MFLOPPY
389 count_only = (mode & COUNT_SAVE);
390 #endif
391 uid = getuid();
392 bwrite(fd, (void *) &uid, sizeof uid);
393 bwrite(fd, (void *) &flags, sizeof(struct flag));
394 bwrite(fd, (void *) &u, sizeof(struct you));
396 /* save random monsters*/
399 int monstcursor = PM_PLAYERMON + 1;
400 while (monstcursor < NUMMONS) {
401 bwrite(fd, (void *) &mons[monstcursor], sizeof(struct permonst));
402 monstcursor++;
406 /* must come before migrating_objs and migrating_mons are freed */
407 save_timers(fd, mode, RANGE_GLOBAL);
408 save_light_sources(fd, mode, RANGE_GLOBAL);
410 if (CHAIN_IN_MON) {
411 uchain->nobj = bc_objs;
412 bc_objs = uchain;
414 if (BALL_IN_MON) {
415 uball->nobj = bc_objs;
416 bc_objs = uball;
418 saveobjchn(fd, invent, mode);
419 saveobjchn(fd, bc_objs, mode);
420 saveobjchn(fd, migrating_objs, mode);
421 savemonchn(fd, migrating_mons, mode);
422 if (release_data(mode)) {
423 invent = 0;
424 migrating_objs = 0;
425 migrating_mons = 0;
427 bwrite(fd, (void *) mvitals, sizeof(mvitals));
429 save_dungeon(fd, (boolean)!!perform_bwrite(mode),
430 (boolean)!!release_data(mode));
431 savelevchn(fd, mode);
432 bwrite(fd, (void *) &moves, sizeof moves);
433 bwrite(fd, (void *) &monstermoves, sizeof monstermoves);
434 bwrite(fd, (void *) &quest_status, sizeof(struct q_score));
435 bwrite(fd, (void *) spl_book,
436 sizeof(struct spell) * (MAXSPELL + 1));
437 bwrite(fd, (void *) tech_list,
438 sizeof(struct tech) * (MAXTECH + 1));
439 save_oracles(fd, mode);
440 if(ustuck_id)
441 bwrite(fd, (void *) &ustuck_id, sizeof ustuck_id);
442 if(usteed_id)
443 bwrite(fd, (void *) &usteed_id, sizeof usteed_id);
444 bwrite(fd, (void *) pl_character, sizeof pl_character);
445 bwrite(fd, (void *) pl_fruit, sizeof pl_fruit);
446 bwrite(fd, (void *) &current_fruit, sizeof current_fruit);
447 savefruitchn(fd, mode);
448 savenames(fd, mode);
449 save_artifacts(fd);
450 save_waterlevel(fd, mode);
451 #ifdef RECORD_ACHIEVE
452 bwrite(fd, (void *) &achieve, sizeof achieve);
453 bwrite(fd, (void *) &achieveX, sizeof achieveX);
454 #endif
455 #if defined(RECORD_REALTIME) || defined(REALTIME_ON_BOTL)
456 realtime = get_realtime();
457 bwrite(fd, (void *) &realtime, sizeof realtime);
458 #endif
460 bflush(fd);
463 #ifdef INSURANCE
464 void
465 savestateinlock()
467 int fd, hpid;
468 static boolean havestate = TRUE;
469 char whynot[BUFSZ];
471 /* When checkpointing is on, the full state needs to be written
472 * on each checkpoint. When checkpointing is off, only the pid
473 * needs to be in the level.0 file, so it does not need to be
474 * constantly rewritten. When checkpointing is turned off during
475 * a game, however, the file has to be rewritten once to truncate
476 * it and avoid restoring from outdated information.
478 * Restricting havestate to this routine means that an additional
479 * noop pid rewriting will take place on the first "checkpoint" after
480 * the game is started or restored, if checkpointing is off.
482 if (flags.ins_chkpt || havestate) {
483 /* save the rest of the current game state in the lock file,
484 * following the original int pid, the current level number,
485 * and the current savefile name, which should not be subject
486 * to any internal compression schemes since they must be
487 * readable by an external utility
489 fd = open_levelfile(0, whynot);
490 if (fd < 0) {
491 pline("%s", whynot);
492 pline("Probably someone removed it.");
493 /*killer = whynot;
494 done(TRICKED);*/
495 return;
498 (void) read(fd, (void *) &hpid, sizeof(hpid));
499 if (hackpid != hpid) {
500 sprintf(whynot,
501 "Level #0 pid (%d) doesn't match ours (%d)!",
502 hpid, hackpid);
503 pline("%s", whynot);
504 /*killer = whynot;
505 done(TRICKED);*/
507 (void) close(fd);
509 fd = create_levelfile(0, whynot);
510 if (fd < 0) {
511 pline("%s", whynot);
512 /*killer = whynot;
513 done(TRICKED);*/
514 return;
516 (void) write(fd, (void *) &hackpid, sizeof(hackpid));
517 if (flags.ins_chkpt) {
518 int currlev = ledger_no(&u.uz);
520 (void) write(fd, (void *) &currlev, sizeof(currlev));
521 save_savefile_name(fd);
522 store_version(fd);
523 #ifdef STORE_PLNAME_IN_FILE
524 bwrite(fd, (void *) plname, PL_NSIZ);
525 bwrite(fd, (void *) plalias, PL_NSIZ);
526 #endif
527 ustuck_id = (u.ustuck ? u.ustuck->m_id : 0);
528 usteed_id = (u.usteed ? u.usteed->m_id : 0);
529 savegamestate(fd, WRITE_SAVE);
531 bclose(fd);
533 havestate = flags.ins_chkpt;
535 #endif
537 #ifdef MFLOPPY
538 boolean
539 savelev(fd, lev, mode)
540 int fd;
541 xchar lev;
542 int mode;
544 if (mode & COUNT_SAVE) {
545 bytes_counted = 0;
546 savelev0(fd, lev, COUNT_SAVE);
547 /* probably bytes_counted will be filled in again by an
548 * immediately following WRITE_SAVE anyway, but we'll
549 * leave it out of checkspace just in case */
550 if (iflags.checkspace) {
551 while (bytes_counted > freediskspace(levels))
552 if (!swapout_oldest())
553 return FALSE;
556 if (mode & (WRITE_SAVE | FREE_SAVE)) {
557 bytes_counted = 0;
558 savelev0(fd, lev, mode);
560 if (mode != FREE_SAVE) {
561 level_info[lev].where = ACTIVE;
562 level_info[lev].time = moves;
563 level_info[lev].size = bytes_counted;
565 return TRUE;
568 STATIC_OVL void
569 savelev0(fd,lev,mode)
570 #else
571 void
572 savelev(fd,lev,mode)
573 #endif
574 int fd;
575 xchar lev;
576 int mode;
578 #ifdef TOS
579 short tlev;
580 #endif
582 /* if we're tearing down the current level without saving anything
583 (which happens upon entrance to the endgame or after an aborted
584 restore attempt) then we don't want to do any actual I/O */
585 if (mode == FREE_SAVE) goto skip_lots;
586 if (iflags.purge_monsters) {
587 /* purge any dead monsters (necessary if we're starting
588 * a panic save rather than a normal one, or sometimes
589 * when changing levels without taking time -- e.g.
590 * create statue trap then immediately level teleport) */
591 dmonsfree();
594 if(fd < 0) panic("Save on bad file!"); /* impossible */
595 #ifdef MFLOPPY
596 count_only = (mode & COUNT_SAVE);
597 #endif
598 if (lev >= 0 && lev <= maxledgerno())
599 level_info[lev].flags |= VISITED;
600 bwrite(fd,(void *) &hackpid,sizeof(hackpid));
601 #ifdef TOS
602 tlev=lev; tlev &= 0x00ff;
603 bwrite(fd,(void *) &tlev,sizeof(tlev));
604 #else
605 bwrite(fd,(void *) &lev,sizeof(lev));
606 #endif
607 #ifdef RLECOMP
609 /* perform run-length encoding of rm structs */
610 struct rm *prm, *rgrm;
611 int x, y;
612 uchar match;
614 rgrm = &levl[0][0]; /* start matching at first rm */
615 match = 0;
617 for (y = 0; y < ROWNO; y++) {
618 for (x = 0; x < COLNO; x++) {
619 prm = &levl[x][y];
620 #ifdef DISPLAY_LAYERS
621 if (prm->mem_bg == rgrm->mem_bg
622 && prm->mem_trap == rgrm->mem_trap
623 && prm->mem_obj == rgrm->mem_obj
624 && prm->mem_corpse == rgrm->mem_corpse
625 && prm->mem_invis == rgrm->mem_invis
626 #else
627 if (prm->glyph == rgrm->glyph
628 #endif
629 && prm->typ == rgrm->typ
630 && prm->seenv == rgrm->seenv
631 && prm->horizontal == rgrm->horizontal
632 && prm->flags == rgrm->flags
633 && prm->lit == rgrm->lit
634 && prm->waslit == rgrm->waslit
635 && prm->roomno == rgrm->roomno
636 && prm->edge == rgrm->edge) {
637 match++;
638 if (match > 254) {
639 match = 254; /* undo this match */
640 goto writeout;
642 } else {
643 /* the run has been broken,
644 * write out run-length encoding */
645 writeout:
646 bwrite(fd, (void *)&match, sizeof(uchar));
647 bwrite(fd, (void *)rgrm, sizeof(struct rm));
648 /* start encoding again. we have at least 1 rm
649 * in the next run, viz. this one. */
650 match = 1;
651 rgrm = prm;
655 if (match > 0) {
656 bwrite(fd, (void *)&match, sizeof(uchar));
657 bwrite(fd, (void *)rgrm, sizeof(struct rm));
660 #else
661 bwrite(fd,(void *) levl,sizeof(levl));
662 #endif /* RLECOMP */
664 bwrite(fd,(void *) &monstermoves,sizeof(monstermoves));
665 bwrite(fd,(void *) &upstair,sizeof(stairway));
666 bwrite(fd,(void *) &dnstair,sizeof(stairway));
667 bwrite(fd,(void *) &upladder,sizeof(stairway));
668 bwrite(fd,(void *) &dnladder,sizeof(stairway));
669 bwrite(fd,(void *) &sstairs,sizeof(stairway));
670 bwrite(fd,(void *) &updest,sizeof(dest_area));
671 bwrite(fd,(void *) &dndest,sizeof(dest_area));
672 bwrite(fd,(void *) &level.flags,sizeof(level.flags));
673 bwrite(fd, (void *) doors, sizeof(doors));
674 save_rooms(fd); /* no dynamic memory to reclaim */
676 /* from here on out, saving also involves allocated memory cleanup */
677 skip_lots:
678 /* must be saved before mons, objs, and buried objs */
679 save_timers(fd, mode, RANGE_LEVEL);
680 save_light_sources(fd, mode, RANGE_LEVEL);
682 savemonchn(fd, fmon, mode);
683 save_worm(fd, mode); /* save worm information */
684 savetrapchn(fd, ftrap, mode);
685 saveobjchn(fd, fobj, mode);
686 saveobjchn(fd, level.buriedobjlist, mode);
687 saveobjchn(fd, billobjs, mode);
688 if (release_data(mode)) {
689 fmon = 0;
690 ftrap = 0;
691 fobj = 0;
692 level.buriedobjlist = 0;
693 billobjs = 0;
695 save_engravings(fd, mode);
696 savedamage(fd, mode);
697 save_regions(fd, mode);
698 if (mode != FREE_SAVE) bflush(fd);
701 #ifdef ZEROCOMP
702 /* The runs of zero-run compression are flushed after the game state or a
703 * level is written out. This adds a couple bytes to a save file, where
704 * the runs could be mashed together, but it allows gluing together game
705 * state and level files to form a save file, and it means the flushing
706 * does not need to be specifically called for every other time a level
707 * file is written out.
710 #define RLESC '\0' /* Leading character for run of LRESC's */
711 #define flushoutrun(ln) (bputc(RLESC), bputc(ln), ln = -1)
713 #ifndef ZEROCOMP_BUFSIZ
714 # define ZEROCOMP_BUFSIZ BUFSZ
715 #endif
716 static NEARDATA unsigned char outbuf[ZEROCOMP_BUFSIZ];
717 static NEARDATA unsigned short outbufp = 0;
718 static NEARDATA short outrunlength = -1;
719 static NEARDATA int bwritefd;
720 static NEARDATA boolean compressing = FALSE;
722 /*dbg()
724 HUP printf("outbufp %d outrunlength %d\n", outbufp,outrunlength);
727 STATIC_OVL void
728 bputc(c)
729 int c;
731 #ifdef MFLOPPY
732 bytes_counted++;
733 if (count_only)
734 return;
735 #endif
736 if (outbufp >= sizeof outbuf) {
737 (void) write(bwritefd, outbuf, sizeof outbuf);
738 outbufp = 0;
740 outbuf[outbufp++] = (unsigned char)c;
743 /*ARGSUSED*/
744 void
745 bufon(fd)
746 int fd;
748 compressing = TRUE;
749 return;
752 /*ARGSUSED*/
753 void
754 bufoff(fd)
755 int fd;
757 if (outbufp) {
758 outbufp = 0;
759 panic("closing file with buffered data still unwritten");
761 outrunlength = -1;
762 compressing = FALSE;
763 return;
766 void
767 bflush(fd) /* flush run and buffer */
768 register int fd;
770 bwritefd = fd;
771 if (outrunlength >= 0) { /* flush run */
772 flushoutrun(outrunlength);
774 #ifdef MFLOPPY
775 if (count_only) outbufp = 0;
776 #endif
778 if (outbufp) {
779 if (write(fd, outbuf, outbufp) != outbufp) {
780 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
781 if (program_state.done_hup)
782 terminate(EXIT_FAILURE);
783 else
784 #endif
785 bclose(fd); /* panic (outbufp != 0) */
787 outbufp = 0;
791 void
792 bwrite(fd, loc, num)
793 int fd;
794 void * loc;
795 register unsigned num;
797 register unsigned char *bp = (unsigned char *)loc;
799 if (!compressing) {
800 #ifdef MFLOPPY
801 bytes_counted += num;
802 if (count_only) return;
803 #endif
804 if ((unsigned) write(fd, loc, num) != num) {
805 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
806 if (program_state.done_hup)
807 terminate(EXIT_FAILURE);
808 else
809 #endif
810 panic("cannot write %u bytes to file #%d", num, fd);
812 } else {
813 bwritefd = fd;
814 for (; num; num--, bp++) {
815 if (*bp == RLESC) { /* One more char in run */
816 if (++outrunlength == 0xFF) {
817 flushoutrun(outrunlength);
819 } else { /* end of run */
820 if (outrunlength >= 0) { /* flush run */
821 flushoutrun(outrunlength);
823 bputc(*bp);
829 void
830 bclose(fd)
831 int fd;
833 bufoff(fd);
834 (void) close(fd);
835 return;
838 #else /* ZEROCOMP */
840 static int bw_fd = -1;
841 static FILE *bw_FILE = 0;
842 static boolean buffering = FALSE;
844 void
845 bufon(fd)
846 int fd;
848 #ifdef UNIX
849 if(bw_fd >= 0)
850 panic("double buffering unexpected");
851 bw_fd = fd;
852 if((bw_FILE = fdopen(fd, "w")) == 0)
853 panic("buffering of file %d failed", fd);
854 #endif
855 buffering = TRUE;
858 void
859 bufoff(fd)
860 int fd;
862 bflush(fd);
863 buffering = FALSE;
866 void
867 bflush(fd)
868 int fd;
870 #ifdef UNIX
871 if(fd == bw_fd) {
872 if(fflush(bw_FILE) == EOF)
873 panic("flush of savefile failed!");
875 #endif
876 return;
879 void
880 bwrite(fd,loc,num)
881 register int fd;
882 register void * loc;
883 register unsigned num;
885 boolean failed;
887 #ifdef MFLOPPY
888 bytes_counted += num;
889 if (count_only) return;
890 #endif
892 #ifdef UNIX
893 if (buffering) {
894 if(fd != bw_fd)
895 panic("unbuffered write to fd %d (!= %d)", fd, bw_fd);
897 failed = (fwrite(loc, (int)num, 1, bw_FILE) != 1);
898 } else
899 #endif /* UNIX */
901 /* lint wants the 3rd arg of write to be an int; lint -p an unsigned */
902 #if defined(BSD) || defined(ULTRIX)
903 failed = (write(fd, loc, (int)num) != (int)num);
904 #else /* e.g. SYSV, __TURBOC__ */
905 failed = (write(fd, loc, num) != num);
906 #endif
909 if (failed) {
910 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
911 if (program_state.done_hup)
912 terminate(EXIT_FAILURE);
913 else
914 #endif
915 panic("cannot write %u bytes to file #%d", num, fd);
919 void
920 bclose(fd)
921 int fd;
923 bufoff(fd);
924 #ifdef UNIX
925 if (fd == bw_fd) {
926 (void) fclose(bw_FILE);
927 bw_fd = -1;
928 bw_FILE = 0;
929 } else
930 #endif
931 (void) close(fd);
932 return;
934 #endif /* ZEROCOMP */
936 STATIC_OVL void
937 savelevchn(fd, mode)
938 register int fd, mode;
940 s_level *tmplev, *tmplev2;
941 int cnt = 0;
943 for (tmplev = sp_levchn; tmplev; tmplev = tmplev->next) cnt++;
944 if (perform_bwrite(mode))
945 bwrite(fd, (void *) &cnt, sizeof(int));
947 for (tmplev = sp_levchn; tmplev; tmplev = tmplev2) {
948 tmplev2 = tmplev->next;
949 if (perform_bwrite(mode))
950 bwrite(fd, (void *) tmplev, sizeof(s_level));
951 if (release_data(mode))
952 free((void *) tmplev);
954 if (release_data(mode))
955 sp_levchn = 0;
958 STATIC_OVL void
959 savedamage(fd, mode)
960 register int fd, mode;
962 register struct damage *damageptr, *tmp_dam;
963 unsigned int xl = 0;
965 damageptr = level.damagelist;
966 for (tmp_dam = damageptr; tmp_dam; tmp_dam = tmp_dam->next)
967 xl++;
968 if (perform_bwrite(mode))
969 bwrite(fd, (void *) &xl, sizeof(xl));
971 while (xl--) {
972 if (perform_bwrite(mode))
973 bwrite(fd, (void *) damageptr, sizeof(*damageptr));
974 tmp_dam = damageptr;
975 damageptr = damageptr->next;
976 if (release_data(mode))
977 free((void *)tmp_dam);
979 if (release_data(mode))
980 level.damagelist = 0;
983 STATIC_OVL void
984 saveobjchn(fd, otmp, mode)
985 register int fd, mode;
986 register struct obj *otmp;
988 register struct obj *otmp2;
989 unsigned int xl;
990 int minusone = -1;
992 while(otmp) {
993 otmp2 = otmp->nobj;
994 if (perform_bwrite(mode)) {
995 xl = otmp->oxlth + otmp->onamelth;
996 bwrite(fd, (void *) &xl, sizeof(int));
997 bwrite(fd, (void *) otmp, xl + sizeof(struct obj));
999 if (Has_contents(otmp))
1000 saveobjchn(fd,otmp->cobj,mode);
1001 if (release_data(mode)) {
1002 if (otmp->oclass == FOOD_CLASS) food_disappears(otmp);
1003 if (otmp->oclass == SPBOOK_CLASS) book_disappears(otmp);
1004 otmp->where = OBJ_FREE; /* set to free so dealloc will work */
1005 otmp->timed = 0; /* not timed any more */
1006 otmp->lamplit = 0; /* caller handled lights */
1007 dealloc_obj(otmp);
1009 otmp = otmp2;
1011 if (perform_bwrite(mode))
1012 bwrite(fd, (void *) &minusone, sizeof(int));
1015 STATIC_OVL void
1016 savemonchn(fd, mtmp, mode)
1017 register int fd, mode;
1018 register struct monst *mtmp;
1020 register struct monst *mtmp2;
1021 unsigned int xl;
1022 int minusone = -1;
1023 struct permonst *monbegin = &mons[0];
1025 if (perform_bwrite(mode))
1026 bwrite(fd, (void *) &monbegin, sizeof(monbegin));
1028 while (mtmp) {
1029 mtmp2 = mtmp->nmon;
1031 if (perform_bwrite(mode)) {
1032 xl = mtmp->mxlth + mtmp->mnamelth;
1033 bwrite(fd, (void *) &xl, sizeof(int));
1034 bwrite(fd, (void *) mtmp, xl + sizeof(struct monst));
1036 if (mtmp->minvent)
1037 saveobjchn(fd,mtmp->minvent,mode);
1038 if (release_data(mode))
1039 dealloc_monst(mtmp);
1040 mtmp = mtmp2;
1042 if (perform_bwrite(mode))
1043 bwrite(fd, (void *) &minusone, sizeof(int));
1046 STATIC_OVL void
1047 savetrapchn(fd, trap, mode)
1048 register int fd, mode;
1049 register struct trap *trap;
1051 register struct trap *trap2;
1052 static struct trap zerotrap;
1054 while (trap) {
1055 trap2 = trap->ntrap;
1056 if (perform_bwrite(mode))
1057 bwrite(fd, (void *) trap, sizeof(struct trap));
1058 if (release_data(mode))
1059 dealloc_trap(trap);
1060 trap = trap2;
1062 if (perform_bwrite(mode))
1063 bwrite(fd, (void *) &zerotrap, sizeof(struct trap));
1066 /* save all the fruit names and ID's; this is used only in saving whole games
1067 * (not levels) and in saving bones levels. When saving a bones level,
1068 * we only want to save the fruits which exist on the bones level; the bones
1069 * level routine marks nonexistent fruits by making the fid negative.
1071 void
1072 savefruitchn(fd, mode)
1073 register int fd, mode;
1075 register struct fruit *f2, *f1;
1076 static struct fruit zerofruit;
1078 f1 = ffruit;
1079 while (f1) {
1080 f2 = f1->nextf;
1081 if (f1->fid >= 0 && perform_bwrite(mode))
1082 bwrite(fd, (void *) f1, sizeof(struct fruit));
1083 if (release_data(mode))
1084 dealloc_fruit(f1);
1085 f1 = f2;
1087 if (perform_bwrite(mode))
1088 bwrite(fd, (void *) &zerofruit, sizeof(struct fruit));
1089 if (release_data(mode))
1090 ffruit = 0;
1093 #if defined(STATUS_COLORS) && defined(TEXTCOLOR)
1095 void
1096 free_percent_color_options(list_head)
1097 const struct percent_color_option *list_head;
1099 if (list_head == NULL) return;
1100 free_percent_color_options(list_head->next);
1101 free(list_head);
1104 void
1105 free_text_color_options(list_head)
1106 const struct text_color_option *list_head;
1108 if (list_head == NULL) return;
1109 free_text_color_options(list_head->next);
1110 free(list_head->text);
1111 free(list_head);
1114 void
1115 free_status_colors()
1117 free_percent_color_options(hp_colors); hp_colors = NULL;
1118 free_percent_color_options(pw_colors); pw_colors = NULL;
1119 free_text_color_options(text_colors); text_colors = NULL;
1121 #endif
1123 /* also called by prscore(); this probably belongs in dungeon.c... */
1125 * [ALI] Also called by init_dungeons() for the sake of the GTK interface
1126 * and the display_score callback of the proxy interface. For this purpose,
1127 * the previous dungeon must be discarded.
1129 void
1130 free_dungeons()
1132 #if defined(FREE_ALL_MEMORY) || defined(GTK_GRAPHICS) || defined(PROXY_GRAPHICS)
1133 savelevchn(0, FREE_SAVE);
1134 save_dungeon(0, FALSE, TRUE);
1135 #endif
1136 return;
1139 #ifdef MENU_COLOR
1140 void
1141 free_menu_coloring()
1143 struct menucoloring *tmp = menu_colorings;
1145 while (tmp) {
1146 struct menucoloring *tmp2 = tmp->next;
1147 #ifdef USE_REGEX_MATCH
1148 (void) regfree(&tmp->match);
1149 #else
1150 free(tmp->match);
1151 #endif
1152 free(tmp);
1153 tmp = tmp2;
1155 return;
1157 #endif
1159 void
1160 freedynamicdata()
1162 #if defined(STATUS_COLORS) && defined(TEXTCOLOR)
1163 free_status_colors();
1164 #endif
1165 unload_qtlist();
1166 free_invbuf(); /* let_to_name (invent.c) */
1167 free_youbuf(); /* You_buf,&c (pline.c) */
1168 msgpline_free();
1169 #ifdef MENU_COLOR
1170 free_menu_coloring();
1171 #endif
1172 tmp_at(DISP_FREEMEM, 0); /* temporary display effects */
1173 #ifdef FREE_ALL_MEMORY
1174 # define freeobjchn(X) (saveobjchn(0, X, FREE_SAVE), X = 0)
1175 # define freemonchn(X) (savemonchn(0, X, FREE_SAVE), X = 0)
1176 # define freetrapchn(X) (savetrapchn(0, X, FREE_SAVE), X = 0)
1177 # define freefruitchn() savefruitchn(0, FREE_SAVE)
1178 # define freenames() savenames(0, FREE_SAVE)
1179 # define free_oracles() save_oracles(0, FREE_SAVE)
1180 # define free_waterlevel() save_waterlevel(0, FREE_SAVE)
1181 # define free_worm() save_worm(0, FREE_SAVE)
1182 # define free_timers(R) save_timers(0, FREE_SAVE, R)
1183 # define free_light_sources(R) save_light_sources(0, FREE_SAVE, R);
1184 # define free_engravings() save_engravings(0, FREE_SAVE)
1185 # define freedamage() savedamage(0, FREE_SAVE)
1186 # define free_animals() mon_animal_list(FALSE)
1188 /* move-specific data */
1189 dmonsfree(); /* release dead monsters */
1191 /* level-specific data */
1192 free_timers(RANGE_LEVEL);
1193 free_light_sources(RANGE_LEVEL);
1194 freemonchn(fmon);
1195 free_worm(); /* release worm segment information */
1196 freetrapchn(ftrap);
1197 freeobjchn(fobj);
1198 freeobjchn(level.buriedobjlist);
1199 freeobjchn(billobjs);
1200 free_engravings();
1201 freedamage();
1203 /* game-state data */
1204 free_timers(RANGE_GLOBAL);
1205 free_light_sources(RANGE_GLOBAL);
1206 freeobjchn(invent);
1207 freeobjchn(migrating_objs);
1208 freemonchn(migrating_mons);
1209 freemonchn(mydogs); /* ascension or dungeon escape */
1210 /* freelevchn(); [folded into free_dungeons()] */
1211 free_animals();
1212 free_oracles();
1213 freefruitchn();
1214 freenames();
1215 free_waterlevel();
1216 free_dungeons();
1218 /* some pointers in iflags */
1219 if (iflags.wc_font_map) free(iflags.wc_font_map);
1220 if (iflags.wc_font_message) free(iflags.wc_font_message);
1221 if (iflags.wc_font_text) free(iflags.wc_font_text);
1222 if (iflags.wc_font_menu) free(iflags.wc_font_menu);
1223 if (iflags.wc_font_status) free(iflags.wc_font_status);
1224 if (iflags.wc_tile_file) free(iflags.wc_tile_file);
1225 #ifdef AUTOPICKUP_EXCEPTIONS
1226 free_autopickup_exceptions();
1227 #endif
1229 #endif /* FREE_ALL_MEMORY */
1230 return;
1233 #ifdef MFLOPPY
1234 boolean
1235 swapin_file(lev)
1236 int lev;
1238 char to[PATHLEN], from[PATHLEN];
1240 sprintf(from, "%s%s", permbones, alllevels);
1241 sprintf(to, "%s%s", levels, alllevels);
1242 set_levelfile_name(from, lev);
1243 set_levelfile_name(to, lev);
1244 if (iflags.checkspace) {
1245 while (level_info[lev].size > freediskspace(to))
1246 if (!swapout_oldest())
1247 return FALSE;
1249 # ifdef WIZARD
1250 if (wizard) {
1251 pline("Swapping in `%s'.", from);
1252 wait_synch();
1254 # endif
1255 copyfile(from, to);
1256 (void) unlink(from);
1257 level_info[lev].where = ACTIVE;
1258 return TRUE;
1261 STATIC_OVL boolean
1262 swapout_oldest() {
1263 char to[PATHLEN], from[PATHLEN];
1264 int i, oldest;
1265 long oldtime;
1267 if (!ramdisk)
1268 return FALSE;
1269 for (i = 1, oldtime = 0, oldest = 0; i <= maxledgerno(); i++)
1270 if (level_info[i].where == ACTIVE
1271 && (!oldtime || level_info[i].time < oldtime)) {
1272 oldest = i;
1273 oldtime = level_info[i].time;
1275 if (!oldest)
1276 return FALSE;
1277 sprintf(from, "%s%s", levels, alllevels);
1278 sprintf(to, "%s%s", permbones, alllevels);
1279 set_levelfile_name(from, oldest);
1280 set_levelfile_name(to, oldest);
1281 # ifdef WIZARD
1282 if (wizard) {
1283 pline("Swapping out `%s'.", from);
1284 wait_synch();
1286 # endif
1287 copyfile(from, to);
1288 (void) unlink(from);
1289 level_info[oldest].where = SWAPPED;
1290 return TRUE;
1293 STATIC_OVL void
1294 copyfile(from, to)
1295 char *from, *to;
1297 # ifdef TOS
1299 if (_copyfile(from, to))
1300 panic("Can't copy %s to %s", from, to);
1301 # else
1302 char buf[BUFSIZ]; /* this is system interaction, therefore
1303 * BUFSIZ instead of NetHack's BUFSZ */
1304 int nfrom, nto, fdfrom, fdto;
1306 if ((fdfrom = open(from, O_RDONLY | O_BINARY, FCMASK)) < 0)
1307 panic("Can't copy from %s !?", from);
1308 if ((fdto = open(to, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK)) < 0)
1309 panic("Can't copy to %s", to);
1310 do {
1311 nfrom = read(fdfrom, buf, BUFSIZ);
1312 nto = write(fdto, buf, nfrom);
1313 if (nto != nfrom)
1314 panic("Copyfile failed!");
1315 } while (nfrom == BUFSIZ);
1316 (void) close(fdfrom);
1317 (void) close(fdto);
1318 # endif /* TOS */
1321 void
1322 co_false() /* see comment in bones.c */
1324 count_only = FALSE;
1325 return;
1328 #endif /* MFLOPPY */
1330 /*save.c*/