OSDN Git Service

update year to 2021
[jnethack/source.git] / src / save.c
index a20b2ea..45335ef 100644 (file)
@@ -1,10 +1,11 @@
-/* NetHack 3.6 save.c  $NHDT-Date: 1448241784 2015/11/23 01:23:04 $  $NHDT-Branch: master $:$NHDT-Revision: 1.95 $ */
+/* NetHack 3.6 save.c  $NHDT-Date: 1559994625 2019/06/08 11:50:25 $  $NHDT-Branch: NetHack-3.6 $:$NHDT-Revision: 1.121 $ */
 /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1985. */
+/*-Copyright (c) Michael Allison, 2009. */
 /* NetHack may be freely redistributed.  See license for details. */
 
 /* JNetHack Copyright */
 /* (c) Issei Numata, Naoki Hamada, Shigehiro Miyashita, 1994-2000  */
-/* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2016            */
+/* For 3.4-, Copyright (c) SHIRAKATA Kentaro, 2002-2021            */
 /* JNetHack may be freely redistributed.  See license for details. */
 
 #include "hack.h"
@@ -69,8 +70,6 @@ static struct save_procs {
 #endif
 };
 
-static long nulls[sizeof(struct trap) + sizeof(struct fruit)];
-
 #if defined(UNIX) || defined(VMS) || defined(__EMX__) || defined(WIN32)
 #define HUP if (!program_state.done_hup)
 #else
@@ -83,6 +82,8 @@ static unsigned ustuck_id = 0, usteed_id = 0;
 int
 dosave()
 {
+    if (iflags.debug_fuzzer)
+        return 0;
     clear_nhwindow(WIN_MESSAGE);
 /*JP
     if (yn("Really save?") == 'n') {
@@ -108,7 +109,7 @@ dosave()
             exit_nhwindows("Be seeing you...");
 */
             exit_nhwindows("\82Ü\82½\89ï\82¢\82Ü\82µ\82å\82¤\81D\81D\81D");
-            terminate(EXIT_SUCCESS);
+            nh_terminate(EXIT_SUCCESS);
         } else
             (void) doredraw();
     }
@@ -129,6 +130,8 @@ dosave0()
        a few of things before saving so that they won't be restored in
        an improper state; these will be no-ops for normal save sequence */
     u.uinvulnerable = 0;
+    if (iflags.save_uswallow)
+        u.uswallow = 1, iflags.save_uswallow = 0;
     if (iflags.save_uinwater)
         u.uinwater = 1, iflags.save_uinwater = 0;
     if (iflags.save_uburied)
@@ -150,8 +153,7 @@ dosave0()
         return 0;
 #endif
 
-    HUP if (iflags.window_inited)
-    {
+    HUP if (iflags.window_inited) {
         nh_uncompress(fq_save);
         fd = open_savefile();
         if (fd > 0) {
@@ -195,7 +197,7 @@ dosave0()
     dotcnt = 0;
     dotrow = 2;
     curs(WIN_MAP, 1, 1);
-    if (strncmpi("X11", windowprocs.name, 3))
+    if (!WINDOWPORT("X11"))
         putstr(WIN_MAP, 0, "Saving:");
 #endif
 #ifdef MFLOPPY
@@ -259,7 +261,7 @@ dosave0()
             dotrow++;
             dotcnt = 0;
         }
-        if (strncmpi("X11", windowprocs.name, 3)) {
+        if (!WINDOWPORT("X11")) {
             putstr(WIN_MAP, 0, ".");
         }
         mark_synch();
@@ -298,35 +300,27 @@ savegamestate(fd, mode)
 register int fd, mode;
 {
     unsigned long uid;
+    struct obj * bc_objs = (struct obj *)0;
 
 #ifdef MFLOPPY
     count_only = (mode & COUNT_SAVE);
 #endif
     uid = (unsigned long) getuid();
     bwrite(fd, (genericptr_t) &uid, sizeof uid);
-    bwrite(fd, (genericptr_t) &context, sizeof(struct context_info));
-    bwrite(fd, (genericptr_t) &flags, sizeof(struct flag));
+    bwrite(fd, (genericptr_t) &context, sizeof context);
+    bwrite(fd, (genericptr_t) &flags, sizeof flags);
 #ifdef SYSFLAGS
-    bwrite(fd, (genericptr_t) &sysflags, sizeof(struct sysflag));
+    bwrite(fd, (genericptr_t) &sysflags, sysflags);
 #endif
-#if 0 /*C360-19*/
-    urealtime.realtime += (long) (getnow() - urealtime.restored);
-    bwrite(fd, (genericptr_t) &u, sizeof(struct you));
-    bwrite(fd, yyyymmddhhmmss(ubirthday), 14);
-    bwrite(fd, (genericptr_t) &urealtime.realtime,
-           sizeof(urealtime.realtime));
-    bwrite(fd, yyyymmddhhmmss(urealtime.restored), 14);
-#else
     urealtime.finish_time = getnow();
     urealtime.realtime += (long) (urealtime.finish_time
                                   - urealtime.start_timing);
-    bwrite(fd, (genericptr_t) &u, sizeof(struct you));
+    bwrite(fd, (genericptr_t) &u, sizeof u);
     bwrite(fd, yyyymmddhhmmss(ubirthday), 14);
     bwrite(fd, (genericptr_t) &urealtime.realtime, sizeof urealtime.realtime);
     bwrite(fd, yyyymmddhhmmss(urealtime.start_timing), 14);  /** Why? **/
     /* this is the value to use for the next update of urealtime.realtime */
     urealtime.start_timing = urealtime.finish_time;
-#endif
     save_killers(fd, mode);
 
     /* must come before migrating_objs and migrating_mons are freed */
@@ -334,14 +328,18 @@ register int fd, mode;
     save_light_sources(fd, mode, RANGE_GLOBAL);
 
     saveobjchn(fd, invent, mode);
+
+    /* save ball and chain if they are currently dangling free (i.e. not on
+       floor or in inventory) */
+    if (CHAIN_IN_MON) {
+        uchain->nobj = bc_objs;
+        bc_objs = uchain;
+    }
     if (BALL_IN_MON) {
-        /* prevent loss of ball & chain when swallowed */
-        uball->nobj = uchain;
-        uchain->nobj = (struct obj *) 0;
-        saveobjchn(fd, uball, mode);
-    } else {
-        saveobjchn(fd, (struct obj *) 0, mode);
+        uball->nobj = bc_objs;
+        bc_objs = uball;
     }
+    saveobjchn(fd, bc_objs, mode);
 
     saveobjchn(fd, migrating_objs, mode);
     savemonchn(fd, migrating_mons, mode);
@@ -350,14 +348,14 @@ register int fd, mode;
         migrating_objs = 0;
         migrating_mons = 0;
     }
-    bwrite(fd, (genericptr_t) mvitals, sizeof(mvitals));
+    bwrite(fd, (genericptr_t) mvitals, sizeof mvitals);
 
     save_dungeon(fd, (boolean) !!perform_bwrite(mode),
                  (boolean) !!release_data(mode));
     savelevchn(fd, mode);
     bwrite(fd, (genericptr_t) &moves, sizeof moves);
     bwrite(fd, (genericptr_t) &monstermoves, sizeof monstermoves);
-    bwrite(fd, (genericptr_t) &quest_status, sizeof(struct q_score));
+    bwrite(fd, (genericptr_t) &quest_status, sizeof quest_status);
     bwrite(fd, (genericptr_t) spl_book,
            sizeof(struct spell) * (MAXSPELL + 1));
     save_artifacts(fd);
@@ -420,10 +418,9 @@ savestateinlock()
         if (tricked_fileremoved(fd, whynot))
             return;
 
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-result"
-        (void) read(fd, (genericptr_t) &hpid, sizeof(hpid));
-#pragma GCC diagnostic pop
+_pragma_ignore(-Wunused-result)
+        (void) read(fd, (genericptr_t) &hpid, sizeof hpid);
+_pragma_pop
         if (hackpid != hpid) {
             Sprintf(whynot, "Level #0 pid (%d) doesn't match ours (%d)!",
                     hpid, hackpid);
@@ -440,17 +437,15 @@ savestateinlock()
             done(TRICKED);
             return;
         }
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-result"
-        (void) write(fd, (genericptr_t) &hackpid, sizeof(hackpid));
-#pragma GCC diagnostic pop
+_pragma_ignore(-Wunused-result)
+        (void) write(fd, (genericptr_t) &hackpid, sizeof hackpid);
+_pragma_pop
         if (flags.ins_chkpt) {
             int currlev = ledger_no(&u.uz);
 
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wunused-result"
-            (void) write(fd, (genericptr_t) &currlev, sizeof(currlev));
-#pragma GCC diagnostic pop
+_pragma_ignore(-Wunused-result)
+            (void) write(fd, (genericptr_t) &currlev, sizeof currlev);
+_pragma_pop
             save_savefile_name(fd);
             store_version(fd);
             store_savefileinfo(fd);
@@ -511,56 +506,76 @@ int mode;
     short tlev;
 #endif
 
-    /* if we're tearing down the current level without saving anything
-       (which happens upon entrance to the endgame or after an aborted
-       restore attempt) then we don't want to do any actual I/O */
-    if (mode == FREE_SAVE)
-        goto skip_lots;
+    /*
+     *  Level file contents:
+     *    version info (handled by caller);
+     *    save file info (compression type; also by caller);
+     *    process ID;
+     *    internal level number (ledger number);
+     *    bones info;
+     *    actual level data.
+     *
+     *  If we're tearing down the current level without saving anything
+     *  (which happens at end of game or upon entrance to endgame or
+     *  after an aborted restore attempt) then we don't want to do any
+     *  actual I/O.  So when only freeing, we skip to the bones info
+     *  portion (which has some freeing to do), then jump quite a bit
+     *  further ahead to the middle of the 'actual level data' portion.
+     */
+    if (mode != FREE_SAVE) {
+        /* WRITE_SAVE (probably ORed with FREE_SAVE), or COUNT_SAVE */
 
-    /* purge any dead monsters (necessary if we're starting
-       a panic save rather than a normal one, or sometimes
-       when changing levels without taking time -- e.g.
-       create statue trap then immediately level teleport) */
-    if (iflags.purge_monsters)
-        dmonsfree();
+        /* purge any dead monsters (necessary if we're starting
+           a panic save rather than a normal one, or sometimes
+           when changing levels without taking time -- e.g.
+           create statue trap then immediately level teleport) */
+        if (iflags.purge_monsters)
+            dmonsfree();
 
-    if (fd < 0)
-        panic("Save on bad file!"); /* impossible */
+        if (fd < 0)
+            panic("Save on bad file!"); /* impossible */
 #ifdef MFLOPPY
-    count_only = (mode & COUNT_SAVE);
+        count_only = (mode & COUNT_SAVE);
 #endif
-    if (lev >= 0 && lev <= maxledgerno())
-        level_info[lev].flags |= VISITED;
-    bwrite(fd, (genericptr_t) &hackpid, sizeof(hackpid));
+        if (lev >= 0 && lev <= maxledgerno())
+            level_info[lev].flags |= VISITED;
+        bwrite(fd, (genericptr_t) &hackpid, sizeof hackpid);
 #ifdef TOS
-    tlev = lev;
-    tlev &= 0x00ff;
-    bwrite(fd, (genericptr_t) &tlev, sizeof(tlev));
+        tlev = lev;
+        tlev &= 0x00ff;
+        bwrite(fd, (genericptr_t) &tlev, sizeof tlev);
 #else
-    bwrite(fd, (genericptr_t) &lev, sizeof(lev));
+        bwrite(fd, (genericptr_t) &lev, sizeof lev);
 #endif
+    }
+
+    /* bones info comes before level data; the intent is for an external
+       program ('hearse') to be able to match a bones file with the
+       corresponding log file entry--or perhaps just skip that?--without
+       the guessing that was needed in 3.4.3 and without having to
+       interpret level data to find where to start; unfortunately it
+       still needs to handle all the data compression schemes */
     savecemetery(fd, mode, &level.bonesinfo);
-    savelevl(fd,
-             (boolean) ((sfsaveinfo.sfi1 & SFI1_RLECOMP) == SFI1_RLECOMP));
-    bwrite(fd, (genericptr_t) lastseentyp, sizeof(lastseentyp));
-    bwrite(fd, (genericptr_t) &monstermoves, sizeof(monstermoves));
-    bwrite(fd, (genericptr_t) &upstair, sizeof(stairway));
-    bwrite(fd, (genericptr_t) &dnstair, sizeof(stairway));
-    bwrite(fd, (genericptr_t) &upladder, sizeof(stairway));
-    bwrite(fd, (genericptr_t) &dnladder, sizeof(stairway));
-    bwrite(fd, (genericptr_t) &sstairs, sizeof(stairway));
-    bwrite(fd, (genericptr_t) &updest, sizeof(dest_area));
-    bwrite(fd, (genericptr_t) &dndest, sizeof(dest_area));
-    bwrite(fd, (genericptr_t) &level.flags, sizeof(level.flags));
-    bwrite(fd, (genericptr_t) doors, sizeof(doors));
+    if (mode == FREE_SAVE) /* see above */
+        goto skip_lots;
+
+    savelevl(fd, (boolean) ((sfsaveinfo.sfi1 & SFI1_RLECOMP) == SFI1_RLECOMP));
+    bwrite(fd, (genericptr_t) lastseentyp, sizeof lastseentyp);
+    bwrite(fd, (genericptr_t) &monstermoves, sizeof monstermoves);
+    bwrite(fd, (genericptr_t) &upstair, sizeof (stairway));
+    bwrite(fd, (genericptr_t) &dnstair, sizeof (stairway));
+    bwrite(fd, (genericptr_t) &upladder, sizeof (stairway));
+    bwrite(fd, (genericptr_t) &dnladder, sizeof (stairway));
+    bwrite(fd, (genericptr_t) &sstairs, sizeof (stairway));
+    bwrite(fd, (genericptr_t) &updest, sizeof (dest_area));
+    bwrite(fd, (genericptr_t) &dndest, sizeof (dest_area));
+    bwrite(fd, (genericptr_t) &level.flags, sizeof level.flags);
+    bwrite(fd, (genericptr_t) doors, sizeof doors);
     save_rooms(fd); /* no dynamic memory to reclaim */
 
-/* from here on out, saving also involves allocated memory cleanup */
-skip_lots:
-    /* this comes before the map, so need cleanup here if we skipped */
-    if (mode == FREE_SAVE)
-        savecemetery(fd, mode, &level.bonesinfo);
-    /* must be saved before mons, objs, and buried objs */
+    /* from here on out, saving also involves allocated memory cleanup */
+ skip_lots:
+    /* timers and lights must be saved before monsters and objects */
     save_timers(fd, mode, RANGE_LEVEL);
     save_light_sources(fd, mode, RANGE_LEVEL);
 
@@ -571,15 +586,18 @@ skip_lots:
     saveobjchn(fd, level.buriedobjlist, mode);
     saveobjchn(fd, billobjs, mode);
     if (release_data(mode)) {
+        int x,y;
+
+        for (y = 0; y < ROWNO; y++)
+            for (x = 0; x < COLNO; x++)
+                level.monsters[x][y] = 0;
         fmon = 0;
         ftrap = 0;
-        fobj = 0;
-        level.buriedobjlist = 0;
-        billobjs = 0;
+        fobj = level.buriedobjlist = billobjs = 0;
         /* level.bonesinfo = 0; -- handled by savecemetery() */
     }
     save_engravings(fd, mode);
-    savedamage(fd, mode);
+    savedamage(fd, mode); /* pending shop wall and/or floor repair */
     save_regions(fd, mode);
     if (mode != FREE_SAVE)
         bflush(fd);
@@ -617,21 +635,20 @@ boolean rlecomp;
                         goto writeout;
                     }
                 } else {
-                /* the run has been broken,
-                 * write out run-length encoding */
-                writeout:
-                    bwrite(fd, (genericptr_t) &match, sizeof(uchar));
-                    bwrite(fd, (genericptr_t) rgrm, sizeof(struct rm));
+                    /* run has been broken, write out run-length encoding */
+ writeout:
+                    bwrite(fd, (genericptr_t) &match, sizeof (uchar));
+                    bwrite(fd, (genericptr_t) rgrm, sizeof (struct rm));
                     /* start encoding again. we have at least 1 rm
-                     * in the next run, viz. this one. */
+                       in the next run, viz. this one. */
                     match = 1;
                     rgrm = prm;
                 }
             }
         }
         if (match > 0) {
-            bwrite(fd, (genericptr_t) &match, sizeof(uchar));
-            bwrite(fd, (genericptr_t) rgrm, sizeof(struct rm));
+            bwrite(fd, (genericptr_t) &match, sizeof (uchar));
+            bwrite(fd, (genericptr_t) rgrm, sizeof (struct rm));
         }
         return;
     }
@@ -761,7 +778,7 @@ register unsigned num;
     if (failed) {
 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
         if (program_state.done_hup)
-            terminate(EXIT_FAILURE);
+            nh_terminate(EXIT_FAILURE);
         else
 #endif
             panic("cannot write %u bytes to file #%d", num, fd);
@@ -867,7 +884,7 @@ register int fd;
         if (write(fd, outbuf, outbufp) != outbufp) {
 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
             if (program_state.done_hup)
-                terminate(EXIT_FAILURE);
+                nh_terminate(EXIT_FAILURE);
             else
 #endif
                 zerocomp_bclose(fd); /* panic (outbufp != 0) */
@@ -893,7 +910,7 @@ register unsigned num;
         if ((unsigned) write(fd, loc, num) != num) {
 #if defined(UNIX) || defined(VMS) || defined(__EMX__)
             if (program_state.done_hup)
-                terminate(EXIT_FAILURE);
+                nh_terminate(EXIT_FAILURE);
             else
 #endif
                 panic("cannot write %u bytes to file #%d", num, fd);
@@ -935,12 +952,12 @@ register int fd, mode;
     for (tmplev = sp_levchn; tmplev; tmplev = tmplev->next)
         cnt++;
     if (perform_bwrite(mode))
-        bwrite(fd, (genericptr_t) &cnt, sizeof(int));
+        bwrite(fd, (genericptr_t) &cnt, sizeof cnt);
 
     for (tmplev = sp_levchn; tmplev; tmplev = tmplev2) {
         tmplev2 = tmplev->next;
         if (perform_bwrite(mode))
-            bwrite(fd, (genericptr_t) tmplev, sizeof(s_level));
+            bwrite(fd, (genericptr_t) tmplev, sizeof *tmplev);
         if (release_data(mode))
             free((genericptr_t) tmplev);
     }
@@ -984,11 +1001,11 @@ register int fd, mode;
     for (tmp_dam = damageptr; tmp_dam; tmp_dam = tmp_dam->next)
         xl++;
     if (perform_bwrite(mode))
-        bwrite(fd, (genericptr_t) &xl, sizeof(xl));
+        bwrite(fd, (genericptr_t) &xl, sizeof xl);
 
     while (xl--) {
         if (perform_bwrite(mode))
-            bwrite(fd, (genericptr_t) damageptr, sizeof(*damageptr));
+            bwrite(fd, (genericptr_t) damageptr, sizeof *damageptr);
         tmp_dam = damageptr;
         damageptr = damageptr->next;
         if (release_data(mode))
@@ -1005,14 +1022,11 @@ struct obj *otmp;
 {
     int buflen, zerobuf = 0;
 
-    buflen = sizeof(struct obj);
-    bwrite(fd, (genericptr_t) &buflen, sizeof(int));
+    buflen = (int) sizeof (struct obj);
+    bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
     bwrite(fd, (genericptr_t) otmp, buflen);
     if (otmp->oextra) {
-        if (ONAME(otmp))
-            buflen = strlen(ONAME(otmp)) + 1;
-        else
-            buflen = 0;
+        buflen = ONAME(otmp) ? (int) strlen(ONAME(otmp)) + 1 : 0;
         bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
         if (buflen > 0)
             bwrite(fd, (genericptr_t) ONAME(otmp), buflen);
@@ -1023,26 +1037,18 @@ struct obj *otmp;
         else
             bwrite(fd, (genericptr_t) &zerobuf, sizeof zerobuf);
 
-        if (OMID(otmp))
-            buflen = sizeof(unsigned);
-        else
-            buflen = 0;
+        buflen = OMID(otmp) ? (int) sizeof (unsigned) : 0;
         bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
         if (buflen > 0)
             bwrite(fd, (genericptr_t) OMID(otmp), buflen);
 
-        if (OLONG(otmp))
-            buflen = sizeof(long);
-        else
-            buflen = 0;
+        /* TODO: post 3.6.x, get rid of this */
+        buflen = OLONG(otmp) ? (int) sizeof (long) : 0;
         bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
         if (buflen > 0)
             bwrite(fd, (genericptr_t) OLONG(otmp), buflen);
 
-        if (OMAILCMD(otmp))
-            buflen = strlen(OMAILCMD(otmp)) + 1;
-        else
-            buflen = 0;
+        buflen = OMAILCMD(otmp) ? (int) strlen(OMAILCMD(otmp)) + 1 : 0;
         bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
         if (buflen > 0)
             bwrite(fd, (genericptr_t) OMAILCMD(otmp), buflen);
@@ -1065,37 +1071,22 @@ register struct obj *otmp;
         if (Has_contents(otmp))
             saveobjchn(fd, otmp->cobj, mode);
         if (release_data(mode)) {
-            /*  if (otmp->oclass == FOOD_CLASS)
-             *      food_disappears(otmp);
-             */
             /*
-             * If these are on the floor, the discarding could
-             * be because of a game save, or we could just be changing levels.
+             * If these are on the floor, the discarding could be
+             * due to game save, or we could just be changing levels.
              * Always invalidate the pointer, but ensure that we have
              * the o_id in order to restore the pointer on reload.
              */
             if (otmp == context.victual.piece) {
-                /* Store the o_id of the victual if mismatched */
-                if (context.victual.o_id != otmp->o_id)
-                    context.victual.o_id = otmp->o_id;
-                /* invalidate the pointer; on reload it will get restored */
+                context.victual.o_id = otmp->o_id;
                 context.victual.piece = (struct obj *) 0;
             }
             if (otmp == context.tin.tin) {
-                /* Store the o_id of your tin */
-                if (context.tin.o_id != otmp->o_id)
-                    context.tin.o_id = otmp->o_id;
-                /* invalidate the pointer; on reload it will get restored */
+                context.tin.o_id = otmp->o_id;
                 context.tin.tin = (struct obj *) 0;
             }
-            /*  if (otmp->oclass == SPBOOK_CLASS)
-             *      book_disappears(otmp);
-             */
             if (otmp == context.spbook.book) {
-                /* Store the o_id of your spellbook */
-                if (context.spbook.o_id != otmp->o_id)
-                    context.spbook.o_id = otmp->o_id;
-                /* invalidate the pointer; on reload it will get restored */
+                context.spbook.o_id = otmp->o_id;
                 context.spbook.book = (struct obj *) 0;
             }
             otmp->where = OBJ_FREE; /* set to free so dealloc will work */
@@ -1108,7 +1099,7 @@ register struct obj *otmp;
         otmp = otmp2;
     }
     if (perform_bwrite(mode))
-        bwrite(fd, (genericptr_t) &minusone, sizeof(int));
+        bwrite(fd, (genericptr_t) &minusone, sizeof (int));
 }
 
 STATIC_OVL void
@@ -1118,58 +1109,36 @@ struct monst *mtmp;
 {
     int buflen;
 
-    buflen = sizeof(struct monst);
-    bwrite(fd, (genericptr_t) &buflen, sizeof(int));
+    mtmp->mtemplit = 0; /* normally clear; if set here then a panic save
+                         * is being written while bhit() was executing */
+    buflen = (int) sizeof (struct monst);
+    bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
     bwrite(fd, (genericptr_t) mtmp, buflen);
     if (mtmp->mextra) {
-        if (MNAME(mtmp))
-            buflen = strlen(MNAME(mtmp)) + 1;
-        else
-            buflen = 0;
+        buflen = MNAME(mtmp) ? (int) strlen(MNAME(mtmp)) + 1 : 0;
         bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
         if (buflen > 0)
             bwrite(fd, (genericptr_t) MNAME(mtmp), buflen);
-
-        if (EGD(mtmp))
-            buflen = sizeof(struct egd);
-        else
-            buflen = 0;
-        bwrite(fd, (genericptr_t) &buflen, sizeof(int));
+        buflen = EGD(mtmp) ? (int) sizeof (struct egd) : 0;
+        bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
         if (buflen > 0)
             bwrite(fd, (genericptr_t) EGD(mtmp), buflen);
-
-        if (EPRI(mtmp))
-            buflen = sizeof(struct epri);
-        else
-            buflen = 0;
-        bwrite(fd, (genericptr_t) &buflen, sizeof(int));
+        buflen = EPRI(mtmp) ? (int) sizeof (struct epri) : 0;
+        bwrite(fd, (genericptr_t) &buflen, sizeof buflen);
         if (buflen > 0)
             bwrite(fd, (genericptr_t) EPRI(mtmp), buflen);
-
-        if (ESHK(mtmp))
-            buflen = sizeof(struct eshk);
-        else
-            buflen = 0;
+        buflen = ESHK(mtmp) ? (int) sizeof (struct eshk) : 0;
         bwrite(fd, (genericptr_t) &buflen, sizeof(int));
         if (buflen > 0)
             bwrite(fd, (genericptr_t) ESHK(mtmp), buflen);
-
-        if (EMIN(mtmp))
-            buflen = sizeof(struct emin);
-        else
-            buflen = 0;
+        buflen = EMIN(mtmp) ? (int) sizeof (struct emin) : 0;
         bwrite(fd, (genericptr_t) &buflen, sizeof(int));
         if (buflen > 0)
             bwrite(fd, (genericptr_t) EMIN(mtmp), buflen);
-
-        if (EDOG(mtmp))
-            buflen = sizeof(struct edog);
-        else
-            buflen = 0;
+        buflen = EDOG(mtmp) ? (int) sizeof (struct edog) : 0;
         bwrite(fd, (genericptr_t) &buflen, sizeof(int));
         if (buflen > 0)
             bwrite(fd, (genericptr_t) EDOG(mtmp), buflen);
-
         /* mcorpsenm is inline int rather than pointer to something,
            so doesn't need to be preceded by a length field */
         bwrite(fd, (genericptr_t) &MCORPSENM(mtmp), sizeof MCORPSENM(mtmp));
@@ -1205,26 +1174,29 @@ register struct monst *mtmp;
         mtmp = mtmp2;
     }
     if (perform_bwrite(mode))
-        bwrite(fd, (genericptr_t) &minusone, sizeof(int));
+        bwrite(fd, (genericptr_t) &minusone, sizeof (int));
 }
 
+/* save traps; ftrap is the only trap chain so the 2nd arg is superfluous */
 STATIC_OVL void
 savetrapchn(fd, trap, mode)
-register int fd, mode;
+int fd;
 register struct trap *trap;
+int mode;
 {
+    static struct trap zerotrap;
     register struct trap *trap2;
 
     while (trap) {
         trap2 = trap->ntrap;
         if (perform_bwrite(mode))
-            bwrite(fd, (genericptr_t) trap, sizeof(struct trap));
+            bwrite(fd, (genericptr_t) trap, sizeof *trap);
         if (release_data(mode))
             dealloc_trap(trap);
         trap = trap2;
     }
     if (perform_bwrite(mode))
-        bwrite(fd, (genericptr_t) nulls, sizeof(struct trap));
+        bwrite(fd, (genericptr_t) &zerotrap, sizeof zerotrap);
 }
 
 /* save all the fruit names and ID's; this is used only in saving whole games
@@ -1234,21 +1206,22 @@ register struct trap *trap;
  */
 void
 savefruitchn(fd, mode)
-register int fd, mode;
+int fd, mode;
 {
+    static struct fruit zerofruit;
     register struct fruit *f2, *f1;
 
     f1 = ffruit;
     while (f1) {
         f2 = f1->nextf;
         if (f1->fid >= 0 && perform_bwrite(mode))
-            bwrite(fd, (genericptr_t) f1, sizeof(struct fruit));
+            bwrite(fd, (genericptr_t) f1, sizeof *f1);
         if (release_data(mode))
             dealloc_fruit(f1);
         f1 = f2;
     }
     if (perform_bwrite(mode))
-        bwrite(fd, (genericptr_t) nulls, sizeof(struct fruit));
+        bwrite(fd, (genericptr_t) &zerofruit, sizeof zerofruit);
     if (release_data(mode))
         ffruit = 0;
 }
@@ -1258,9 +1231,10 @@ store_plname_in_file(fd)
 int fd;
 {
     int plsiztmp = PL_NSIZ;
+
     bufoff(fd);
     /* bwrite() before bufon() uses plain write() */
-    bwrite(fd, (genericptr_t) &plsiztmp, sizeof(plsiztmp));
+    bwrite(fd, (genericptr_t) &plsiztmp, sizeof plsiztmp);
     bwrite(fd, (genericptr_t) plname, plsiztmp);
     bufon(fd);
     return;
@@ -1280,15 +1254,17 @@ int fd, mode;
         while ((msg = getmsghistory(init)) != 0) {
             init = FALSE;
             msglen = strlen(msg);
+            if (msglen < 1)
+                continue;
             /* sanity: truncate if necessary (shouldn't happen);
                no need to modify msg[] since terminator isn't written */
             if (msglen > BUFSZ - 1)
                 msglen = BUFSZ - 1;
-            bwrite(fd, (genericptr_t) &msglen, sizeof(msglen));
+            bwrite(fd, (genericptr_t) &msglen, sizeof msglen);
             bwrite(fd, (genericptr_t) msg, msglen);
             ++msgcount;
         }
-        bwrite(fd, (genericptr_t) &minusone, sizeof(int));
+        bwrite(fd, (genericptr_t) &minusone, sizeof (int));
     }
     debugpline1("Stored %d messages into savefile.", msgcount);
     /* note: we don't attempt to handle release_data() here */
@@ -1304,15 +1280,14 @@ int fd;
      * sfsaveinfo (decl.c) describes the savefile info that actually
      * gets written into the savefile, and is used to determine the
      * save file being written.
-
+     *
      * sfrestinfo (decl.c) describes the savefile info that is
      * being used to read the information from an existing savefile.
-     *
      */
 
     bufoff(fd);
     /* bwrite() before bufon() uses plain write() */
-    bwrite(fd, (genericptr_t) &sfsaveinfo, (unsigned) (sizeof sfsaveinfo));
+    bwrite(fd, (genericptr_t) &sfsaveinfo, (unsigned) sizeof sfsaveinfo);
     bufon(fd);
     return;
 }
@@ -1367,6 +1342,9 @@ free_dungeons()
 void
 freedynamicdata()
 {
+#if defined(UNIX) && defined(MAIL)
+    free_maildata();
+#endif
     unload_qtlist();
     free_menu_coloring();
     free_invbuf();           /* let_to_name (invent.c) */
@@ -1374,38 +1352,25 @@ freedynamicdata()
     msgtype_free();
     tmp_at(DISP_FREEMEM, 0); /* temporary display effects */
 #ifdef FREE_ALL_MEMORY
+#define free_current_level() savelev(-1, -1, FREE_SAVE)
 #define freeobjchn(X) (saveobjchn(0, X, FREE_SAVE), X = 0)
 #define freemonchn(X) (savemonchn(0, X, FREE_SAVE), X = 0)
-#define freetrapchn(X) (savetrapchn(0, X, FREE_SAVE), X = 0)
 #define freefruitchn() savefruitchn(0, FREE_SAVE)
 #define freenames() savenames(0, FREE_SAVE)
 #define free_killers() save_killers(0, FREE_SAVE)
 #define free_oracles() save_oracles(0, FREE_SAVE)
 #define free_waterlevel() save_waterlevel(0, FREE_SAVE)
-#define free_worm() save_worm(0, FREE_SAVE)
 #define free_timers(R) save_timers(0, FREE_SAVE, R)
-#define free_light_sources(R) save_light_sources(0, FREE_SAVE, R);
-#define free_engravings() save_engravings(0, FREE_SAVE)
-#define freedamage() savedamage(0, FREE_SAVE)
+#define free_light_sources(R) save_light_sources(0, FREE_SAVE, R)
 #define free_animals() mon_animal_list(FALSE)
 
     /* move-specific data */
     dmonsfree(); /* release dead monsters */
 
     /* level-specific data */
-    free_timers(RANGE_LEVEL);
-    free_light_sources(RANGE_LEVEL);
-    clear_regions();
-    freemonchn(fmon);
-    free_worm(); /* release worm segment information */
-    freetrapchn(ftrap);
-    freeobjchn(fobj);
-    freeobjchn(level.buriedobjlist);
-    freeobjchn(billobjs);
-    free_engravings();
-    freedamage();
-
-    /* game-state data */
+    free_current_level();
+
+    /* game-state data [ought to reorganize savegamestate() to handle this] */
     free_killers();
     free_timers(RANGE_GLOBAL);
     free_light_sources(RANGE_GLOBAL);
@@ -1423,25 +1388,28 @@ freedynamicdata()
 
     /* some pointers in iflags */
     if (iflags.wc_font_map)
-        free(iflags.wc_font_map);
+        free((genericptr_t) iflags.wc_font_map), iflags.wc_font_map = 0;
     if (iflags.wc_font_message)
-        free(iflags.wc_font_message);
+        free((genericptr_t) iflags.wc_font_message),
+        iflags.wc_font_message = 0;
     if (iflags.wc_font_text)
-        free(iflags.wc_font_text);
+        free((genericptr_t) iflags.wc_font_text), iflags.wc_font_text = 0;
     if (iflags.wc_font_menu)
-        free(iflags.wc_font_menu);
+        free((genericptr_t) iflags.wc_font_menu), iflags.wc_font_menu = 0;
     if (iflags.wc_font_status)
-        free(iflags.wc_font_status);
+        free((genericptr_t) iflags.wc_font_status), iflags.wc_font_status = 0;
     if (iflags.wc_tile_file)
-        free(iflags.wc_tile_file);
+        free((genericptr_t) iflags.wc_tile_file), iflags.wc_tile_file = 0;
     free_autopickup_exceptions();
 
     /* miscellaneous */
     /* free_pickinv_cache();  --  now done from really_done()... */
     free_symsets();
 #endif /* FREE_ALL_MEMORY */
-#ifdef STATUS_VIA_WINDOWPORT
-    status_finish();
+    if (VIA_WINDOWPORT())
+        status_finish();
+#ifdef DUMPLOG
+    dumplogfreemessages();
 #endif
 
     /* last, because it frees data that might be used by panic() to provide