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