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. */ 4 5#include "hack.h" 6#include "lev.h" 7#include "quest.h" 8#include <assert.h> 9 10#ifndef NO_SIGNAL 11#include <signal.h> 12#endif 13#if !defined(LSC) && !defined(O_WRONLY) && !defined(AZTEC_C) 14#include <fcntl.h> 15#endif 16 17#ifdef MFLOPPY 18long bytes_counted; 19static int count_only; 20#endif 21 22#ifdef MICRO 23int dotcnt, dotrow; /* also used in restore */ 24#endif 25 26#ifdef ZEROCOMP 27STATIC_DCL void FDECL(bputc, (int)); 28#endif 29STATIC_DCL void FDECL(savelevchn, (int,int)); 30STATIC_DCL void FDECL(savedamage, (int,int)); 31STATIC_DCL void FDECL(saveobjchn, (int,struct obj *,int)); 32STATIC_DCL void FDECL(savemonchn, (int,struct monst *,int)); 33STATIC_DCL void FDECL(savetrapchn, (int,struct trap *,int)); 34STATIC_DCL void FDECL(savegamestate, (int,int)); 35#ifdef MFLOPPY 36STATIC_DCL void FDECL(savelev0, (int,XCHAR_P,int)); 37STATIC_DCL boolean NDECL(swapout_oldest); 38STATIC_DCL void FDECL(copyfile, (char *,char *)); 39#endif /* MFLOPPY */ 40#ifdef GCC_WARN 41static long nulls[10]; 42#else 43#define nulls nul 44#endif 45 46#if defined(UNIX) || defined(VMS) || defined(__EMX__) || defined(WIN32) 47#define HUP if (!program_state.done_hup) 48#else 49#define HUP 50#endif 51 52/* need to preserve these during save to avoid accessing freed memory */ 53static unsigned ustuck_id = 0, usteed_id = 0; 54 55int 56dosave() 57{ 58 clear_nhwindow(WIN_MESSAGE); 59 if(yn("Really save?") == 'n') { 60 clear_nhwindow(WIN_MESSAGE); 61 if(multi > 0) nomul(0); 62 } else { 63 clear_nhwindow(WIN_MESSAGE); 64 pline("Saving..."); 65#if defined(UNIX) || defined(VMS) || defined(__EMX__) 66 program_state.done_hup = 0; 67#endif 68 if(dosave0()) { 69 program_state.something_worth_saving = 0; 70 u.uhp = -1; /* universal game's over indicator */ 71 /* make sure they see the Saving message */ 72 display_nhwindow(WIN_MESSAGE, TRUE); 73 exit_nhwindows("Be seeing you..."); 74 terminate(EXIT_SUCCESS); 75 } else (void)doredraw(); 76 } 77 return 0; 78} 79 80 81#if defined(UNIX) || defined(VMS) || defined (__EMX__) || defined(WIN32) 82/*ARGSUSED*/ 83void 84hangup(sig_unused) /* called as signal() handler, so sent at least one arg */ 85int sig_unused; 86{ 87# ifdef NOSAVEONHANGUP 88 (void) signal(SIGINT, SIG_IGN); 89 clearlocks(); 90# ifndef VMS 91 terminate(EXIT_FAILURE); 92# endif 93# else /* SAVEONHANGUP */ 94 if (!program_state.done_hup++) { 95 if (program_state.something_worth_saving) 96 (void) dosave0(); 97# ifdef VMS 98 /* don't call exit when already within an exit handler; 99 that would cancel any other pending user-mode handlers */ 100 if (!program_state.exiting) 101# endif 102 { 103 clearlocks(); 104 terminate(EXIT_FAILURE); 105 } 106 } 107# endif 108 return; 109} 110#endif 111 112/* returns 1 if save successful */ 113int 114dosave0() 115{ 116 #if 0 117 const char *fq_save; 118 register int fd, ofd; 119 xchar ltmp; 120 d_level uz_save; 121 char whynot[BUFSZ]; 122 123 if (!SAVEF[0]) 124 return 0; 125 fq_save = fqname(SAVEF, SAVEPREFIX, 1); /* level files take 0 */ 126 127#if defined(UNIX) || defined(VMS) 128 (void) signal(SIGHUP, SIG_IGN); 129#endif 130#ifndef NO_SIGNAL 131 (void) signal(SIGINT, SIG_IGN); 132#endif 133 134#if defined(MICRO) && defined(MFLOPPY) 135 if (!saveDiskPrompt(0)) return 0; 136#endif 137 138 HUP if (iflags.window_inited) { 139 uncompress(fq_save); 140 fd = open_savefile(); 141 if (fd > 0) { 142 (void) close(fd); 143 clear_nhwindow(WIN_MESSAGE); 144 There("seems to be an old save file."); 145 if (yn("Overwrite the old file?") == 'n') { 146 compress(fq_save); 147 return 0; 148 } 149 } 150 } 151 152 HUP mark_synch(); /* flush any buffered screen output */ 153 154 fd = create_savefile(); 155 if(fd < 0) { 156 HUP pline("Cannot open save file."); 157 (void) delete_savefile(); /* ab@unido */ 158 return(0); 159 } 160 161 vision_recalc(2); /* shut down vision to prevent problems 162 in the event of an impossible() call */ 163 164 /* undo date-dependent luck adjustments made at startup time */ 165 if(flags.moonphase == FULL_MOON) /* ut-sally!fletcher */ 166 change_luck(-1); /* and unido!ab */ 167 if(flags.friday13) 168 change_luck(1); 169 if(iflags.window_inited) 170 HUP clear_nhwindow(WIN_MESSAGE); 171 172#ifdef MICRO 173 dotcnt = 0; 174 dotrow = 2; 175 curs(WIN_MAP, 1, 1); 176 if (strncmpi("X11", windowprocs.name, 3)) 177 putstr(WIN_MAP, 0, "Saving:"); 178#endif 179#ifdef MFLOPPY 180 /* make sure there is enough disk space */ 181 if (iflags.checkspace) { 182 long fds, needed; 183 184 savelev(fd, ledger_no(&u.uz), COUNT_SAVE); 185 savegamestate(fd, COUNT_SAVE); 186 needed = bytes_counted; 187 188 for (ltmp = 1; ltmp <= maxledgerno(); ltmp++) 189 if (ltmp != ledger_no(&u.uz) && level_info[ltmp].where) 190 needed += level_info[ltmp].size + (sizeof ltmp); 191 fds = freediskspace(fq_save); 192 if (needed > fds) { 193 HUP { 194 There("is insufficient space on SAVE disk."); 195 pline("Require %ld bytes but only have %ld.", needed, fds); 196 } 197 flushout(); 198 (void) close(fd); 199 (void) delete_savefile(); 200 return 0; 201 } 202 203 co_false(); 204 } 205#endif /* MFLOPPY */ 206 207 store_version(fd); 208#ifdef STORE_PLNAME_IN_FILE 209 bwrite(fd, (genericptr_t) plname, PL_NSIZ); 210#endif 211 ustuck_id = (u.ustuck ? u.ustuck->m_id : 0); 212#ifdef STEED 213 usteed_id = (u.usteed ? u.usteed->m_id : 0); 214#endif 215 savelev(fd, ledger_no(&u.uz), WRITE_SAVE | FREE_SAVE); 216 savegamestate(fd, WRITE_SAVE | FREE_SAVE); 217 218 /* While copying level files around, zero out u.uz to keep 219 * parts of the restore code from completely initializing all 220 * in-core data structures, since all we're doing is copying. 221 * This also avoids at least one nasty core dump. 222 */ 223 uz_save = u.uz; 224 u.uz.dnum = u.uz.dlevel = 0; 225 /* these pointers are no longer valid, and at least u.usteed 226 * may mislead place_monster() on other levels 227 */ 228 u.ustuck = (struct monst *)0; 229#ifdef STEED 230 u.usteed = (struct monst *)0; 231#endif 232 233 for(ltmp = (xchar)1; ltmp <= maxledgerno(); ltmp++) { 234 if (ltmp == ledger_no(&uz_save)) continue; 235 if (!(level_info[ltmp].flags & LFILE_EXISTS)) continue; 236#ifdef MICRO 237 curs(WIN_MAP, 1 + dotcnt++, dotrow); 238 if (dotcnt >= (COLNO - 1)) { 239 dotrow++; 240 dotcnt = 0; 241 } 242 if (strncmpi("X11", windowprocs.name, 3)){ 243 putstr(WIN_MAP, 0, "."); 244 } 245 mark_synch(); 246#endif 247 ofd = open_levelfile(ltmp, whynot); 248 if (ofd < 0) { 249 HUP pline("%s", whynot); 250 (void) close(fd); 251 (void) delete_savefile(); 252 HUP killer = whynot; 253 HUP done(TRICKED); 254 return(0); 255 } 256 minit(); /* ZEROCOMP */ 257 getlev(ofd, hackpid, ltmp, FALSE); 258 (void) close(ofd); 259 bwrite(fd, (genericptr_t) <mp, sizeof ltmp); /* level number*/ 260 savelev(fd, ltmp, WRITE_SAVE | FREE_SAVE); /* actual level*/ 261 delete_levelfile(ltmp); 262 } 263 bclose(fd); 264 265 u.uz = uz_save; 266 267 /* get rid of current level --jgm */ 268 delete_levelfile(ledger_no(&u.uz)); 269 delete_levelfile(0); 270 compress(fq_save); 271 #endif 272 // No save support in RefOS. 273 return(1); 274} 275 276STATIC_OVL void 277savegamestate(fd, mode) 278register int fd, mode; 279{ 280 int uid; 281 282#ifdef MFLOPPY 283 count_only = (mode & COUNT_SAVE); 284#endif 285 uid = getuid(); 286 bwrite(fd, (genericptr_t) &uid, sizeof uid); 287 bwrite(fd, (genericptr_t) &flags, sizeof(struct flag)); 288 bwrite(fd, (genericptr_t) &u, sizeof(struct you)); 289 290 /* must come before migrating_objs and migrating_mons are freed */ 291 save_timers(fd, mode, RANGE_GLOBAL); 292 save_light_sources(fd, mode, RANGE_GLOBAL); 293 294 saveobjchn(fd, invent, mode); 295 saveobjchn(fd, migrating_objs, mode); 296 savemonchn(fd, migrating_mons, mode); 297 if (release_data(mode)) { 298 invent = 0; 299 migrating_objs = 0; 300 migrating_mons = 0; 301 } 302 bwrite(fd, (genericptr_t) mvitals, sizeof(mvitals)); 303 304 save_dungeon(fd, (boolean)!!perform_bwrite(mode), 305 (boolean)!!release_data(mode)); 306 savelevchn(fd, mode); 307 bwrite(fd, (genericptr_t) &moves, sizeof moves); 308 bwrite(fd, (genericptr_t) &monstermoves, sizeof monstermoves); 309 bwrite(fd, (genericptr_t) &quest_status, sizeof(struct q_score)); 310 bwrite(fd, (genericptr_t) spl_book, 311 sizeof(struct spell) * (MAXSPELL + 1)); 312 save_artifacts(fd); 313 save_oracles(fd, mode); 314 if(ustuck_id) 315 bwrite(fd, (genericptr_t) &ustuck_id, sizeof ustuck_id); 316#ifdef STEED 317 if(usteed_id) 318 bwrite(fd, (genericptr_t) &usteed_id, sizeof usteed_id); 319#endif 320 bwrite(fd, (genericptr_t) pl_character, sizeof pl_character); 321 bwrite(fd, (genericptr_t) pl_fruit, sizeof pl_fruit); 322 bwrite(fd, (genericptr_t) ¤t_fruit, sizeof current_fruit); 323 savefruitchn(fd, mode); 324 savenames(fd, mode); 325 save_waterlevel(fd, mode); 326 bflush(fd); 327} 328 329#ifdef INSURANCE 330void 331savestateinlock() 332{ 333 int fd, hpid; 334 static boolean havestate = TRUE; 335 char whynot[BUFSZ]; 336 337 /* When checkpointing is on, the full state needs to be written 338 * on each checkpoint. When checkpointing is off, only the pid 339 * needs to be in the level.0 file, so it does not need to be 340 * constantly rewritten. When checkpointing is turned off during 341 * a game, however, the file has to be rewritten once to truncate 342 * it and avoid restoring from outdated information. 343 * 344 * Restricting havestate to this routine means that an additional 345 * noop pid rewriting will take place on the first "checkpoint" after 346 * the game is started or restored, if checkpointing is off. 347 */ 348 if (flags.ins_chkpt || havestate) { 349 /* save the rest of the current game state in the lock file, 350 * following the original int pid, the current level number, 351 * and the current savefile name, which should not be subject 352 * to any internal compression schemes since they must be 353 * readable by an external utility 354 */ 355 fd = open_levelfile(0, whynot); 356 if (fd < 0) { 357 pline("%s", whynot); 358 pline("Probably someone removed it."); 359 killer = whynot; 360 done(TRICKED); 361 return; 362 } 363 364 (void) read(fd, (genericptr_t) &hpid, sizeof(hpid)); 365 if (hackpid != hpid) { 366 Sprintf(whynot, 367 "Level #0 pid (%d) doesn't match ours (%d)!", 368 hpid, hackpid); 369 pline("%s", whynot); 370 killer = whynot; 371 done(TRICKED); 372 } 373 (void) close(fd); 374 375 fd = create_levelfile(0, whynot); 376 if (fd < 0) { 377 pline("%s", whynot); 378 killer = whynot; 379 done(TRICKED); 380 return; 381 } 382 (void) write(fd, (genericptr_t) &hackpid, sizeof(hackpid)); 383 if (flags.ins_chkpt) { 384 int currlev = ledger_no(&u.uz); 385 386 (void) write(fd, (genericptr_t) &currlev, sizeof(currlev)); 387 save_savefile_name(fd); 388 store_version(fd); 389#ifdef STORE_PLNAME_IN_FILE 390 bwrite(fd, (genericptr_t) plname, PL_NSIZ); 391#endif 392 ustuck_id = (u.ustuck ? u.ustuck->m_id : 0); 393#ifdef STEED 394 usteed_id = (u.usteed ? u.usteed->m_id : 0); 395#endif 396 savegamestate(fd, WRITE_SAVE); 397 } 398 bclose(fd); 399 } 400 havestate = flags.ins_chkpt; 401} 402#endif 403 404#ifdef MFLOPPY 405boolean 406savelev(fd, lev, mode) 407int fd; 408xchar lev; 409int mode; 410{ 411 if (mode & COUNT_SAVE) { 412 bytes_counted = 0; 413 savelev0(fd, lev, COUNT_SAVE); 414 /* probably bytes_counted will be filled in again by an 415 * immediately following WRITE_SAVE anyway, but we'll 416 * leave it out of checkspace just in case */ 417 if (iflags.checkspace) { 418 while (bytes_counted > freediskspace(levels)) 419 if (!swapout_oldest()) 420 return FALSE; 421 } 422 } 423 if (mode & (WRITE_SAVE | FREE_SAVE)) { 424 bytes_counted = 0; 425 savelev0(fd, lev, mode); 426 } 427 if (mode != FREE_SAVE) { 428 level_info[lev].where = ACTIVE; 429 level_info[lev].time = moves; 430 level_info[lev].size = bytes_counted; 431 } 432 return TRUE; 433} 434 435STATIC_OVL void 436savelev0(fd,lev,mode) 437#else 438void 439savelev(fd,lev,mode) 440#endif 441int fd; 442xchar lev; 443int mode; 444{ 445#ifdef TOS 446 short tlev; 447#endif 448 449 /* if we're tearing down the current level without saving anything 450 (which happens upon entrance to the endgame or after an aborted 451 restore attempt) then we don't want to do any actual I/O */ 452 if (mode == FREE_SAVE) goto skip_lots; 453 if (iflags.purge_monsters) { 454 /* purge any dead monsters (necessary if we're starting 455 * a panic save rather than a normal one, or sometimes 456 * when changing levels without taking time -- e.g. 457 * create statue trap then immediately level teleport) */ 458 dmonsfree(); 459 } 460 461 if(fd < 0) panic("Save on bad file!"); /* impossible */ 462#ifdef MFLOPPY 463 count_only = (mode & COUNT_SAVE); 464#endif 465 if (lev >= 0 && lev <= maxledgerno()) 466 level_info[lev].flags |= VISITED; 467 bwrite(fd,(genericptr_t) &hackpid,sizeof(hackpid)); 468#ifdef TOS 469 tlev=lev; tlev &= 0x00ff; 470 bwrite(fd,(genericptr_t) &tlev,sizeof(tlev)); 471#else 472 bwrite(fd,(genericptr_t) &lev,sizeof(lev)); 473#endif 474#ifdef RLECOMP 475 { 476 /* perform run-length encoding of rm structs */ 477 struct rm *prm, *rgrm; 478 int x, y; 479 uchar match; 480 481 rgrm = &levl[0][0]; /* start matching at first rm */ 482 match = 0; 483 484 for (y = 0; y < ROWNO; y++) { 485 for (x = 0; x < COLNO; x++) { 486 prm = &levl[x][y]; 487 if (prm->glyph == rgrm->glyph 488 && prm->typ == rgrm->typ 489 && prm->seenv == rgrm->seenv 490 && prm->horizontal == rgrm->horizontal 491 && prm->flags == rgrm->flags 492 && prm->lit == rgrm->lit 493 && prm->waslit == rgrm->waslit 494 && prm->roomno == rgrm->roomno 495 && prm->edge == rgrm->edge) { 496 match++; 497 if (match > 254) { 498 match = 254; /* undo this match */ 499 goto writeout; 500 } 501 } else { 502 /* the run has been broken, 503 * write out run-length encoding */ 504 writeout: 505 bwrite(fd, (genericptr_t)&match, sizeof(uchar)); 506 bwrite(fd, (genericptr_t)rgrm, sizeof(struct rm)); 507 /* start encoding again. we have at least 1 rm 508 * in the next run, viz. this one. */ 509 match = 1; 510 rgrm = prm; 511 } 512 } 513 } 514 if (match > 0) { 515 bwrite(fd, (genericptr_t)&match, sizeof(uchar)); 516 bwrite(fd, (genericptr_t)rgrm, sizeof(struct rm)); 517 } 518 } 519#else 520 bwrite(fd,(genericptr_t) levl,sizeof(levl)); 521#endif /* RLECOMP */ 522 523 bwrite(fd,(genericptr_t) &monstermoves,sizeof(monstermoves)); 524 bwrite(fd,(genericptr_t) &upstair,sizeof(stairway)); 525 bwrite(fd,(genericptr_t) &dnstair,sizeof(stairway)); 526 bwrite(fd,(genericptr_t) &upladder,sizeof(stairway)); 527 bwrite(fd,(genericptr_t) &dnladder,sizeof(stairway)); 528 bwrite(fd,(genericptr_t) &sstairs,sizeof(stairway)); 529 bwrite(fd,(genericptr_t) &updest,sizeof(dest_area)); 530 bwrite(fd,(genericptr_t) &dndest,sizeof(dest_area)); 531 bwrite(fd,(genericptr_t) &level.flags,sizeof(level.flags)); 532 bwrite(fd, (genericptr_t) doors, sizeof(doors)); 533 save_rooms(fd); /* no dynamic memory to reclaim */ 534 535 /* from here on out, saving also involves allocated memory cleanup */ 536 skip_lots: 537 /* must be saved before mons, objs, and buried objs */ 538 save_timers(fd, mode, RANGE_LEVEL); 539 save_light_sources(fd, mode, RANGE_LEVEL); 540 541 savemonchn(fd, fmon, mode); 542 save_worm(fd, mode); /* save worm information */ 543 savetrapchn(fd, ftrap, mode); 544 saveobjchn(fd, fobj, mode); 545 saveobjchn(fd, level.buriedobjlist, mode); 546 saveobjchn(fd, billobjs, mode); 547 if (release_data(mode)) { 548 fmon = 0; 549 ftrap = 0; 550 fobj = 0; 551 level.buriedobjlist = 0; 552 billobjs = 0; 553 } 554 save_engravings(fd, mode); 555 savedamage(fd, mode); 556 save_regions(fd, mode); 557 if (mode != FREE_SAVE) bflush(fd); 558} 559 560#ifdef ZEROCOMP 561/* The runs of zero-run compression are flushed after the game state or a 562 * level is written out. This adds a couple bytes to a save file, where 563 * the runs could be mashed together, but it allows gluing together game 564 * state and level files to form a save file, and it means the flushing 565 * does not need to be specifically called for every other time a level 566 * file is written out. 567 */ 568 569#define RLESC '\0' /* Leading character for run of LRESC's */ 570#define flushoutrun(ln) (bputc(RLESC), bputc(ln), ln = -1) 571 572#ifndef ZEROCOMP_BUFSIZ 573# define ZEROCOMP_BUFSIZ BUFSZ 574#endif 575static NEARDATA unsigned char outbuf[ZEROCOMP_BUFSIZ]; 576static NEARDATA unsigned short outbufp = 0; 577static NEARDATA short outrunlength = -1; 578static NEARDATA int bwritefd; 579static NEARDATA boolean compressing = FALSE; 580 581/*dbg() 582{ 583 HUP printf("outbufp %d outrunlength %d\n", outbufp,outrunlength); 584}*/ 585 586STATIC_OVL void 587bputc(c) 588int c; 589{ 590#ifdef MFLOPPY 591 bytes_counted++; 592 if (count_only) 593 return; 594#endif 595 if (outbufp >= sizeof outbuf) { 596 (void) write(bwritefd, outbuf, sizeof outbuf); 597 outbufp = 0; 598 } 599 outbuf[outbufp++] = (unsigned char)c; 600} 601 602/*ARGSUSED*/ 603void 604bufon(fd) 605int fd; 606{ 607 compressing = TRUE; 608 return; 609} 610 611/*ARGSUSED*/ 612void 613bufoff(fd) 614int fd; 615{ 616 if (outbufp) { 617 outbufp = 0; 618 panic("closing file with buffered data still unwritten"); 619 } 620 outrunlength = -1; 621 compressing = FALSE; 622 return; 623} 624 625void 626bflush(fd) /* flush run and buffer */ 627register int fd; 628{ 629 bwritefd = fd; 630 if (outrunlength >= 0) { /* flush run */ 631 flushoutrun(outrunlength); 632 } 633#ifdef MFLOPPY 634 if (count_only) outbufp = 0; 635#endif 636 637 if (outbufp) { 638 if (write(fd, outbuf, outbufp) != outbufp) { 639#if defined(UNIX) || defined(VMS) || defined(__EMX__) 640 if (program_state.done_hup) 641 terminate(EXIT_FAILURE); 642 else 643#endif 644 assert(!"outbufp != 0"); 645 bclose(fd); /* panic (outbufp != 0) */ 646 } 647 outbufp = 0; 648 } 649} 650 651void 652bwrite(fd, loc, num) 653int fd; 654genericptr_t loc; 655register unsigned num; 656{ 657 register unsigned char *bp = (unsigned char *)loc; 658 659 if (!compressing) { 660#ifdef MFLOPPY 661 bytes_counted += num; 662 if (count_only) return; 663#endif 664 if ((unsigned) write(fd, loc, num) != num) { 665#if defined(UNIX) || defined(VMS) || defined(__EMX__) 666 if (program_state.done_hup) 667 terminate(EXIT_FAILURE); 668 else 669#endif 670 panic("cannot write %u bytes to file #%d", num, fd); 671 } 672 } else { 673 bwritefd = fd; 674 for (; num; num--, bp++) { 675 if (*bp == RLESC) { /* One more char in run */ 676 if (++outrunlength == 0xFF) { 677 flushoutrun(outrunlength); 678 } 679 } else { /* end of run */ 680 if (outrunlength >= 0) { /* flush run */ 681 flushoutrun(outrunlength); 682 } 683 bputc(*bp); 684 } 685 } 686 } 687} 688 689void 690bclose(fd) 691int fd; 692{ 693 bufoff(fd); 694 (void) close(fd); 695 return; 696} 697 698#else /* ZEROCOMP */ 699 700static int bw_fd = -1; 701static FILE *bw_FILE = 0; 702static boolean buffering = FALSE; 703 704void 705bufon(fd) 706 int fd; 707{ 708#ifdef UNIX 709 if(bw_fd >= 0) 710 panic("double buffering unexpected"); 711 bw_fd = fd; 712 if((bw_FILE = fdopen(fd, "w")) == 0) 713 panic("buffering of file %d failed", fd); 714#endif 715 buffering = TRUE; 716} 717 718void 719bufoff(fd) 720int fd; 721{ 722 bflush(fd); 723 buffering = FALSE; 724} 725 726void 727bflush(fd) 728 int fd; 729{ 730#ifdef UNIX 731 if(fd == bw_fd) { 732 if(fflush(bw_FILE) == EOF) 733 panic("flush of savefile failed!"); 734 } 735#endif 736 return; 737} 738 739void 740bwrite(fd,loc,num) 741register int fd; 742register genericptr_t loc; 743register unsigned num; 744{ 745 boolean failed; 746 747#ifdef MFLOPPY 748 bytes_counted += num; 749 if (count_only) return; 750#endif 751 752#ifdef UNIX 753 if (buffering) { 754 if(fd != bw_fd) 755 panic("unbuffered write to fd %d (!= %d)", fd, bw_fd); 756 757 failed = (fwrite(loc, (int)num, 1, bw_FILE) != 1); 758 } else 759#endif /* UNIX */ 760 { 761/* lint wants the 3rd arg of write to be an int; lint -p an unsigned */ 762#if defined(BSD) || defined(ULTRIX) 763 failed = (write(fd, loc, (int)num) != (int)num); 764#else /* e.g. SYSV, __TURBOC__ */ 765 failed = (write(fd, loc, num) != num); 766#endif 767 } 768 769 if (failed) { 770#if defined(UNIX) || defined(VMS) || defined(__EMX__) 771 if (program_state.done_hup) 772 terminate(EXIT_FAILURE); 773 else 774#endif 775 panic("cannot write %u bytes to file #%d", num, fd); 776 } 777} 778 779void 780bclose(fd) 781 int fd; 782{ 783 bufoff(fd); 784#ifdef UNIX 785 if (fd == bw_fd) { 786 (void) fclose(bw_FILE); 787 bw_fd = -1; 788 bw_FILE = 0; 789 } else 790#endif 791 (void) close(fd); 792 return; 793} 794#endif /* ZEROCOMP */ 795 796STATIC_OVL void 797savelevchn(fd, mode) 798register int fd, mode; 799{ 800 s_level *tmplev, *tmplev2; 801 int cnt = 0; 802 803 for (tmplev = sp_levchn; tmplev; tmplev = tmplev->next) cnt++; 804 if (perform_bwrite(mode)) 805 bwrite(fd, (genericptr_t) &cnt, sizeof(int)); 806 807 for (tmplev = sp_levchn; tmplev; tmplev = tmplev2) { 808 tmplev2 = tmplev->next; 809 if (perform_bwrite(mode)) 810 bwrite(fd, (genericptr_t) tmplev, sizeof(s_level)); 811 if (release_data(mode)) 812 free((genericptr_t) tmplev); 813 } 814 if (release_data(mode)) 815 sp_levchn = 0; 816} 817 818STATIC_OVL void 819savedamage(fd, mode) 820register int fd, mode; 821{ 822 register struct damage *damageptr, *tmp_dam; 823 unsigned int xl = 0; 824 825 damageptr = level.damagelist; 826 for (tmp_dam = damageptr; tmp_dam; tmp_dam = tmp_dam->next) 827 xl++; 828 if (perform_bwrite(mode)) 829 bwrite(fd, (genericptr_t) &xl, sizeof(xl)); 830 831 while (xl--) { 832 if (perform_bwrite(mode)) 833 bwrite(fd, (genericptr_t) damageptr, sizeof(*damageptr)); 834 tmp_dam = damageptr; 835 damageptr = damageptr->next; 836 if (release_data(mode)) 837 free((genericptr_t)tmp_dam); 838 } 839 if (release_data(mode)) 840 level.damagelist = 0; 841} 842 843STATIC_OVL void 844saveobjchn(fd, otmp, mode) 845register int fd, mode; 846register struct obj *otmp; 847{ 848 register struct obj *otmp2; 849 unsigned int xl; 850 int minusone = -1; 851 852 while(otmp) { 853 otmp2 = otmp->nobj; 854 if (perform_bwrite(mode)) { 855 xl = otmp->oxlth + otmp->onamelth; 856 bwrite(fd, (genericptr_t) &xl, sizeof(int)); 857 bwrite(fd, (genericptr_t) otmp, xl + sizeof(struct obj)); 858 } 859 if (Has_contents(otmp)) 860 saveobjchn(fd,otmp->cobj,mode); 861 if (release_data(mode)) { 862 if (otmp->oclass == FOOD_CLASS) food_disappears(otmp); 863 if (otmp->oclass == SPBOOK_CLASS) book_disappears(otmp); 864 otmp->where = OBJ_FREE; /* set to free so dealloc will work */ 865 otmp->timed = 0; /* not timed any more */ 866 otmp->lamplit = 0; /* caller handled lights */ 867 dealloc_obj(otmp); 868 } 869 otmp = otmp2; 870 } 871 if (perform_bwrite(mode)) 872 bwrite(fd, (genericptr_t) &minusone, sizeof(int)); 873} 874 875STATIC_OVL void 876savemonchn(fd, mtmp, mode) 877register int fd, mode; 878register struct monst *mtmp; 879{ 880 register struct monst *mtmp2; 881 unsigned int xl; 882 int minusone = -1; 883 struct permonst *monbegin = &mons[0]; 884 885 if (perform_bwrite(mode)) 886 bwrite(fd, (genericptr_t) &monbegin, sizeof(monbegin)); 887 888 while (mtmp) { 889 mtmp2 = mtmp->nmon; 890 if (perform_bwrite(mode)) { 891 xl = mtmp->mxlth + mtmp->mnamelth; 892 bwrite(fd, (genericptr_t) &xl, sizeof(int)); 893 bwrite(fd, (genericptr_t) mtmp, xl + sizeof(struct monst)); 894 } 895 if (mtmp->minvent) 896 saveobjchn(fd,mtmp->minvent,mode); 897 if (release_data(mode)) 898 dealloc_monst(mtmp); 899 mtmp = mtmp2; 900 } 901 if (perform_bwrite(mode)) 902 bwrite(fd, (genericptr_t) &minusone, sizeof(int)); 903} 904 905STATIC_OVL void 906savetrapchn(fd, trap, mode) 907register int fd, mode; 908register struct trap *trap; 909{ 910 register struct trap *trap2; 911 912 while (trap) { 913 trap2 = trap->ntrap; 914 if (perform_bwrite(mode)) 915 bwrite(fd, (genericptr_t) trap, sizeof(struct trap)); 916 if (release_data(mode)) 917 dealloc_trap(trap); 918 trap = trap2; 919 } 920 if (perform_bwrite(mode)) 921 bwrite(fd, (genericptr_t)nulls, sizeof(struct trap)); 922} 923 924/* save all the fruit names and ID's; this is used only in saving whole games 925 * (not levels) and in saving bones levels. When saving a bones level, 926 * we only want to save the fruits which exist on the bones level; the bones 927 * level routine marks nonexistent fruits by making the fid negative. 928 */ 929void 930savefruitchn(fd, mode) 931register int fd, mode; 932{ 933 register struct fruit *f2, *f1; 934 935 f1 = ffruit; 936 while (f1) { 937 f2 = f1->nextf; 938 if (f1->fid >= 0 && perform_bwrite(mode)) 939 bwrite(fd, (genericptr_t) f1, sizeof(struct fruit)); 940 if (release_data(mode)) 941 dealloc_fruit(f1); 942 f1 = f2; 943 } 944 if (perform_bwrite(mode)) 945 bwrite(fd, (genericptr_t)nulls, sizeof(struct fruit)); 946 if (release_data(mode)) 947 ffruit = 0; 948} 949 950/* also called by prscore(); this probably belongs in dungeon.c... */ 951void 952free_dungeons() 953{ 954#ifdef FREE_ALL_MEMORY 955 savelevchn(0, FREE_SAVE); 956 save_dungeon(0, FALSE, TRUE); 957#endif 958 return; 959} 960 961void 962freedynamicdata() 963{ 964 unload_qtlist(); 965 free_invbuf(); /* let_to_name (invent.c) */ 966 free_youbuf(); /* You_buf,&c (pline.c) */ 967 tmp_at(DISP_FREEMEM, 0); /* temporary display effects */ 968#ifdef FREE_ALL_MEMORY 969# define freeobjchn(X) (saveobjchn(0, X, FREE_SAVE), X = 0) 970# define freemonchn(X) (savemonchn(0, X, FREE_SAVE), X = 0) 971# define freetrapchn(X) (savetrapchn(0, X, FREE_SAVE), X = 0) 972# define freefruitchn() savefruitchn(0, FREE_SAVE) 973# define freenames() savenames(0, FREE_SAVE) 974# define free_oracles() save_oracles(0, FREE_SAVE) 975# define free_waterlevel() save_waterlevel(0, FREE_SAVE) 976# define free_worm() save_worm(0, FREE_SAVE) 977# define free_timers(R) save_timers(0, FREE_SAVE, R) 978# define free_light_sources(R) save_light_sources(0, FREE_SAVE, R); 979# define free_engravings() save_engravings(0, FREE_SAVE) 980# define freedamage() savedamage(0, FREE_SAVE) 981# define free_animals() mon_animal_list(FALSE) 982 983 /* move-specific data */ 984 dmonsfree(); /* release dead monsters */ 985 986 /* level-specific data */ 987 free_timers(RANGE_LEVEL); 988 free_light_sources(RANGE_LEVEL); 989 freemonchn(fmon); 990 free_worm(); /* release worm segment information */ 991 freetrapchn(ftrap); 992 freeobjchn(fobj); 993 freeobjchn(level.buriedobjlist); 994 freeobjchn(billobjs); 995 free_engravings(); 996 freedamage(); 997 998 /* game-state data */ 999 free_timers(RANGE_GLOBAL); 1000 free_light_sources(RANGE_GLOBAL); 1001 freeobjchn(invent); 1002 freeobjchn(migrating_objs); 1003 freemonchn(migrating_mons); 1004 freemonchn(mydogs); /* ascension or dungeon escape */ 1005 /* freelevchn(); [folded into free_dungeons()] */ 1006 free_animals(); 1007 free_oracles(); 1008 freefruitchn(); 1009 freenames(); 1010 free_waterlevel(); 1011 free_dungeons(); 1012 1013 /* some pointers in iflags */ 1014 if (iflags.wc_font_map) free(iflags.wc_font_map); 1015 if (iflags.wc_font_message) free(iflags.wc_font_message); 1016 if (iflags.wc_font_text) free(iflags.wc_font_text); 1017 if (iflags.wc_font_menu) free(iflags.wc_font_menu); 1018 if (iflags.wc_font_status) free(iflags.wc_font_status); 1019 if (iflags.wc_tile_file) free(iflags.wc_tile_file); 1020#ifdef AUTOPICKUP_EXCEPTIONS 1021 free_autopickup_exceptions(); 1022#endif 1023 1024#endif /* FREE_ALL_MEMORY */ 1025 return; 1026} 1027 1028#ifdef MFLOPPY 1029boolean 1030swapin_file(lev) 1031int lev; 1032{ 1033 char to[PATHLEN], from[PATHLEN]; 1034 1035 Sprintf(from, "%s%s", permbones, alllevels); 1036 Sprintf(to, "%s%s", levels, alllevels); 1037 set_levelfile_name(from, lev); 1038 set_levelfile_name(to, lev); 1039 if (iflags.checkspace) { 1040 while (level_info[lev].size > freediskspace(to)) 1041 if (!swapout_oldest()) 1042 return FALSE; 1043 } 1044# ifdef WIZARD 1045 if (wizard) { 1046 pline("Swapping in `%s'.", from); 1047 wait_synch(); 1048 } 1049# endif 1050 copyfile(from, to); 1051 (void) unlink(from); 1052 level_info[lev].where = ACTIVE; 1053 return TRUE; 1054} 1055 1056STATIC_OVL boolean 1057swapout_oldest() { 1058 char to[PATHLEN], from[PATHLEN]; 1059 int i, oldest; 1060 long oldtime; 1061 1062 if (!ramdisk) 1063 return FALSE; 1064 for (i = 1, oldtime = 0, oldest = 0; i <= maxledgerno(); i++) 1065 if (level_info[i].where == ACTIVE 1066 && (!oldtime || level_info[i].time < oldtime)) { 1067 oldest = i; 1068 oldtime = level_info[i].time; 1069 } 1070 if (!oldest) 1071 return FALSE; 1072 Sprintf(from, "%s%s", levels, alllevels); 1073 Sprintf(to, "%s%s", permbones, alllevels); 1074 set_levelfile_name(from, oldest); 1075 set_levelfile_name(to, oldest); 1076# ifdef WIZARD 1077 if (wizard) { 1078 pline("Swapping out `%s'.", from); 1079 wait_synch(); 1080 } 1081# endif 1082 copyfile(from, to); 1083 (void) unlink(from); 1084 level_info[oldest].where = SWAPPED; 1085 return TRUE; 1086} 1087 1088STATIC_OVL void 1089copyfile(from, to) 1090char *from, *to; 1091{ 1092# ifdef TOS 1093 1094 if (_copyfile(from, to)) 1095 panic("Can't copy %s to %s", from, to); 1096# else 1097 char buf[BUFSIZ]; /* this is system interaction, therefore 1098 * BUFSIZ instead of NetHack's BUFSZ */ 1099 int nfrom, nto, fdfrom, fdto; 1100 1101 if ((fdfrom = open(from, O_RDONLY | O_BINARY, FCMASK)) < 0) 1102 panic("Can't copy from %s !?", from); 1103 if ((fdto = open(to, O_WRONLY | O_BINARY | O_CREAT | O_TRUNC, FCMASK)) < 0) 1104 panic("Can't copy to %s", to); 1105 do { 1106 nfrom = read(fdfrom, buf, BUFSIZ); 1107 nto = write(fdto, buf, nfrom); 1108 if (nto != nfrom) 1109 panic("Copyfile failed!"); 1110 } while (nfrom == BUFSIZ); 1111 (void) close(fdfrom); 1112 (void) close(fdto); 1113# endif /* TOS */ 1114} 1115 1116void 1117co_false() /* see comment in bones.c */ 1118{ 1119 count_only = FALSE; 1120 return; 1121} 1122 1123#endif /* MFLOPPY */ 1124 1125/*save.c*/ 1126