]> git.cameronkatri.com Git - bsdgames-darwin.git/blobdiff - adventure/save.c
cgram: sort includes
[bsdgames-darwin.git] / adventure / save.c
index c2dc9215ff5a15e89cf23a90b49451f60449e8bf..c4054b465885491791c669ada6f2667180983c67 100644 (file)
@@ -1,4 +1,4 @@
-/*     $NetBSD: save.c,v 1.5 1998/09/13 00:07:24 hubertf Exp $ */
+/*     $NetBSD: save.c,v 1.14 2014/03/22 22:04:40 dholland Exp $       */
 
 /*-
  * Copyright (c) 1991, 1993
  * 2. Redistributions in binary form must reproduce the above copyright
  *    notice, this list of conditions and the following disclaimer in the
  *    documentation and/or other materials provided with the distribution.
- * 3. All advertising materials mentioning features or use of this software
- *    must display the following acknowledgement:
- *     This product includes software developed by the University of
- *     California, Berkeley and its contributors.
- * 4. Neither the name of the University nor the names of its contributors
+ * 3. Neither the name of the University nor the names of its contributors
  *    may be used to endorse or promote products derived from this software
  *    without specific prior written permission.
  *
 #if 0
 static char sccsid[] = "@(#)save.c     8.1 (Berkeley) 5/31/93";
 #else
-__RCSID("$NetBSD: save.c,v 1.5 1998/09/13 00:07:24 hubertf Exp $");
+__RCSID("$NetBSD: save.c,v 1.14 2014/03/22 22:04:40 dholland Exp $");
 #endif
 #endif                         /* not lint */
 
+#include <sys/types.h>
+#include <sys/time.h>
+#include <stdbool.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <err.h>
+#include <assert.h>
+
 #include "hdr.h"
 #include "extern.h"
 
-struct savestruct {
+#define __arraycount(a) (sizeof(a) / sizeof(*(a)))
+
+struct savefile {
+       FILE *f;
+       const char *name;
+       bool warned;
+       size_t bintextpos;
+       uint32_t key;
+       struct crcstate crc;
+       unsigned char pad[8];
+       unsigned padpos;
+};
+
+#define BINTEXT_WIDTH 60
+#define FORMAT_VERSION 2
+#define FORMAT_VERSION_NOSUM 1
+static const char header[] = "Adventure save file\n";
+
+////////////////////////////////////////////////////////////
+// base16 output encoding
+
+/*
+ * Map 16 plain values into 90 coded values and back.
+ */
+
+static const char coding[90] =
+       "Db.GOyT]7a6zpF(c*5H9oK~0[WVAg&kR)ml,2^q-1Y3v+"
+       "X/=JirZL$C>_N?:}B{dfnsxU<@MQ%8|P!4h`ESt;euwIj"
+;
+
+static int
+readletter(char letter, unsigned char *ret)
+{
+       const char *s;
+
+       s = strchr(coding, letter);
+       if (s == NULL) {
+               return 1;
+       }
+       *ret = (s - coding) % 16;
+       return 0;
+}
+
+static char
+writeletter(unsigned char nibble)
+{
+       unsigned code;
+
+       assert(nibble < 16);
+       do {
+               code = (16 * (random() % 6)) + nibble;
+       } while (code >= 90);
+       return coding[code];
+}
+
+////////////////////////////////////////////////////////////
+// savefile
+
+/*
+ * Open a savefile.
+ */
+static struct savefile *
+savefile_open(const char *name, bool forwrite)
+{
+       struct savefile *sf;
+
+       sf = malloc(sizeof(*sf));
+       if (sf == NULL) {
+               return NULL;
+       }
+       sf->f = fopen(name, forwrite ? "w" : "r");
+       if (sf->f == NULL) {
+               free(sf);
+               fprintf(stderr,
+                   "Hmm.  The name \"%s\" appears to be magically blocked.\n",
+                   name);
+               return NULL;
+       }
+       sf->name = name;
+       sf->warned = false;
+       sf->bintextpos = 0;
+       sf->key = 0;
+       crc_start(&sf->crc);
+       memset(sf->pad, 0, sizeof(sf->pad));
+       sf->padpos = 0;
+       return sf;
+}
+
+/*
+ * Raw read.
+ */
+static int
+savefile_rawread(struct savefile *sf, void *data, size_t len)
+{
+       size_t result;
+
+       result = fread(data, 1, len, sf->f);
+       if (result != len || ferror(sf->f)) {
+               fprintf(stderr, "Oops: error reading %s.\n", sf->name);
+               sf->warned = true;
+               return 1;
+       }
+       return 0;
+}
+
+/*
+ * Raw write.
+ */
+static int
+savefile_rawwrite(struct savefile *sf, const void *data, size_t len)
+{
+       size_t result;
+
+       result = fwrite(data, 1, len, sf->f);
+       if (result != len || ferror(sf->f)) {
+               fprintf(stderr, "Oops: error writing %s.\n", sf->name);
+               sf->warned = true;
+               return 1;
+       }
+       return 0;
+}
+
+/*
+ * Close a savefile.
+ */
+static int
+savefile_close(struct savefile *sf)
+{
+       int ret;
+
+       if (sf->bintextpos > 0) {
+               savefile_rawwrite(sf, "\n", 1);
+       }
+
+       ret = 0;
+       if (fclose(sf->f)) {
+               if (!sf->warned) {
+                       fprintf(stderr, "Oops: error on %s.\n", sf->name);
+               }
+               ret = 1;
+       }
+       free(sf);
+       return ret;
+}
+
+/*
+ * Read encoded binary data, discarding any whitespace that appears.
+ */
+static int
+savefile_bintextread(struct savefile *sf, void *data, size_t len)
+{
+       size_t pos;
+       unsigned char *udata;
+       int ch;
+
+       udata = data;
+       pos = 0;
+       while (pos < len) {
+               ch = fgetc(sf->f);
+               if (ch == EOF || ferror(sf->f)) {
+                       fprintf(stderr, "Oops: error reading %s.\n", sf->name);
+                       sf->warned = true;
+                       return 1;
+               }
+               if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') {
+                       continue;
+               }
+               udata[pos++] = ch;
+       }
+       return 0;
+}
+
+/*
+ * Read binary data, decoding from text using readletter().
+ */
+static int
+savefile_binread(struct savefile *sf, void *data, size_t len)
+{
+       unsigned char buf[64];
+       unsigned char *udata;
+       unsigned char val1, val2;
+       size_t pos, amt, i;
+
+       udata = data;
+       pos = 0;
+       while (pos < len) {
+               amt = len - pos;
+               if (amt > sizeof(buf) / 2) {
+                       amt = sizeof(buf) / 2;
+               }
+               if (savefile_bintextread(sf, buf, amt*2)) {
+                       return 1;
+               }
+               for (i=0; i<amt; i++) {
+                       if (readletter(buf[i*2], &val1)) {
+                               return 1;
+                       }
+                       if (readletter(buf[i*2 + 1], &val2)) {
+                               return 1;
+                       }
+                       udata[pos++] = val1 * 16 + val2;
+               }
+       }
+       return 0;
+}
+
+/*
+ * Write encoded binary data, inserting newlines to get a neatly
+ * formatted block.
+ */
+static int
+savefile_bintextwrite(struct savefile *sf, const void *data, size_t len)
+{
+       size_t pos, amt;
+       const unsigned char *udata;
+
+       udata = data;
+       pos = 0;
+       while (pos < len) {
+               amt = BINTEXT_WIDTH - sf->bintextpos;
+               if (amt > len - pos) {
+                       amt = len - pos;
+               }
+               if (savefile_rawwrite(sf, udata + pos, amt)) {
+                       return 1;
+               }
+               pos += amt;
+               sf->bintextpos += amt;
+               if (sf->bintextpos >= BINTEXT_WIDTH) {
+                       savefile_rawwrite(sf, "\n", 1);
+                       sf->bintextpos = 0;
+               }
+       }
+       return 0;
+}
+
+/*
+ * Write binary data, encoding as text using writeletter().
+ */
+static int
+savefile_binwrite(struct savefile *sf, const void *data, size_t len)
+{
+       unsigned char buf[64];
+       const unsigned char *udata;
+       size_t pos, bpos;
+       unsigned char byte;
+
+       udata = data;
+       pos = 0;
+       bpos = 0;
+       while (pos < len) {
+               byte = udata[pos++];
+               buf[bpos++] = writeletter(byte >> 4);
+               buf[bpos++] = writeletter(byte & 0xf);
+               if (bpos >= sizeof(buf)) {
+                       if (savefile_bintextwrite(sf, buf, bpos)) {
+                               return 1;
+                       }
+                       bpos = 0;
+               }
+       }
+       if (savefile_bintextwrite(sf, buf, bpos)) {
+               return 1;
+       }
+       return 0;
+}
+
+/*
+ * Lightweight "encryption" for save files. This is not meant to
+ * be secure and wouldn't be even if we didn't write the decrypt
+ * key to the beginning of the save file; it's just meant to be
+ * enough to discourage casual cheating.
+ */
+
+/*
+ * Make cheesy hash of buf[0..buflen]. Note: buf and outhash may overlap.
+ */
+static void
+hash(const void *data, size_t datalen, unsigned char *out, size_t outlen)
+{
+       const unsigned char *udata;
+       size_t i;
+       uint64_t val;
+       const unsigned char *uval;
+       size_t valpos;
+
+       udata = data;
+       val = 0;
+       for (i=0; i<datalen; i++) {
+               val = val ^ 0xbadc0ffee;
+               val = (val << 4) | (val >> 60);
+               val += udata[i] ^ 0xbeefU;
+       }
+
+       uval = (unsigned char *)&val;
+       valpos = 0;
+       for (i=0; i<outlen; i++) {
+               out[i] = uval[valpos++];
+               if (valpos >= sizeof(val)) {
+                       valpos = 0;
+               }
+       }
+}
+
+/*
+ * Set the "encryption" key.
+ */
+static void
+savefile_key(struct savefile *sf, uint32_t key)
+{
+       sf->key = 0;
+       crc_start(&sf->crc);
+       hash(&sf->key, sizeof(sf->key), sf->pad, sizeof(sf->pad));
+       sf->padpos = 0;
+}
+
+/*
+ * Get an "encryption" pad byte. This forms a stream "cipher" that we
+ * xor with the plaintext save data.
+ */
+static unsigned char
+savefile_getpad(struct savefile *sf)
+{
+       unsigned char ret;
+
+       ret = sf->pad[sf->padpos++];
+       if (sf->padpos >= sizeof(sf->pad)) {
+               hash(sf->pad, sizeof(sf->pad), sf->pad, sizeof(sf->pad));
+               sf->padpos = 0;
+       }
+       return ret;
+}
+
+/*
+ * Read "encrypted" data.
+ */
+static int
+savefile_cread(struct savefile *sf, void *data, size_t len)
+{
+       char buf[64];
+       unsigned char *udata;
+       size_t pos, amt, i;
+       unsigned char ch;
+
+       udata = data;
+       pos = 0;
+       while (pos < len) {
+               amt = len - pos;
+               if (amt > sizeof(buf)) {
+                       amt = sizeof(buf);
+               }
+               if (savefile_binread(sf, buf, amt)) {
+                       return 1;
+               }
+               for (i=0; i<amt; i++) {
+                       ch = buf[i];
+                       ch ^= savefile_getpad(sf);
+                       udata[pos + i] = ch;
+               }
+               pos += amt;
+       }
+       crc_add(&sf->crc, data, len);
+       return 0;
+}
+
+/*
+ * Write "encrypted" data.
+ */
+static int
+savefile_cwrite(struct savefile *sf, const void *data, size_t len)
+{
+       char buf[64];
+       const unsigned char *udata;
+       size_t pos, amt, i;
+       unsigned char ch;
+
+       udata = data;
+       pos = 0;
+       while (pos < len) {
+               amt = len - pos;
+               if (amt > sizeof(buf)) {
+                       amt = sizeof(buf);
+               }
+               for (i=0; i<amt; i++) {
+                       ch = udata[pos + i];
+                       ch ^= savefile_getpad(sf);
+                       buf[i] = ch;
+               }
+               if (savefile_binwrite(sf, buf, amt)) {
+                       return 1;
+               }
+               pos += amt;
+       }
+       crc_add(&sf->crc, data, len);
+       return 0;
+}
+
+////////////////////////////////////////////////////////////
+// compat for old save files
+
+struct compat_saveinfo {
        void   *address;
-       int     width;
+       size_t  width;
 };
 
-struct savestruct save_array[] =
+static const struct compat_saveinfo compat_savearray[] =
 {
        {&abbnum, sizeof(abbnum)},
        {&attack, sizeof(attack)},
@@ -68,8 +470,8 @@ struct savestruct save_array[] =
        {&clock1, sizeof(clock1)},
        {&clock2, sizeof(clock2)},
        {&closed, sizeof(closed)},
-       {&closng, sizeof(closng)},
-       {&daltlc, sizeof(daltlc)},
+       {&isclosing, sizeof(isclosing)},
+       {&daltloc, sizeof(daltloc)},
        {&demo, sizeof(demo)},
        {&detail, sizeof(detail)},
        {&dflag, sizeof(dflag)},
@@ -77,27 +479,27 @@ struct savestruct save_array[] =
        {&dtotal, sizeof(dtotal)},
        {&foobar, sizeof(foobar)},
        {&gaveup, sizeof(gaveup)},
-       {&holdng, sizeof(holdng)},
+       {&holding, sizeof(holding)},
        {&iwest, sizeof(iwest)},
        {&k, sizeof(k)},
        {&k2, sizeof(k2)},
        {&knfloc, sizeof(knfloc)},
        {&kq, sizeof(kq)},
-       {&latncy, sizeof(latncy)},
+       {&latency, sizeof(latency)},
        {&limit, sizeof(limit)},
        {&lmwarn, sizeof(lmwarn)},
        {&loc, sizeof(loc)},
        {&maxdie, sizeof(maxdie)},
-       {&mxscor, sizeof(mxscor)},
+       {&maxscore, sizeof(maxscore)},
        {&newloc, sizeof(newloc)},
        {&numdie, sizeof(numdie)},
        {&obj, sizeof(obj)},
-       {&oldlc2, sizeof(oldlc2)},
+       {&oldloc2, sizeof(oldloc2)},
        {&oldloc, sizeof(oldloc)},
        {&panic, sizeof(panic)},
-       {&saved, sizeof(saved)},
+       {&saveday, sizeof(saveday)},
        {&savet, sizeof(savet)},
-       {&scorng, sizeof(scorng)},
+       {&scoring, sizeof(scoring)},
        {&spk, sizeof(spk)},
        {&stick, sizeof(stick)},
        {&tally, sizeof(tally)},
@@ -107,7 +509,7 @@ struct savestruct save_array[] =
        {&verb, sizeof(verb)},
        {&wd1, sizeof(wd1)},
        {&wd2, sizeof(wd2)},
-       {&wzdark, sizeof(wzdark)},
+       {&wasdark, sizeof(wasdark)},
        {&yea, sizeof(yea)},
        {atloc, sizeof(atloc)},
        {dloc, sizeof(dloc)},
@@ -123,48 +525,15 @@ struct savestruct save_array[] =
        {NULL, 0}
 };
 
-int
-save(outfile)                  /* Two passes on data: first to get checksum,
-                                * second */
-       const char   *outfile;  /* to output the data using checksum to start
-                                * random #s */
-{
-       FILE   *out;
-       struct savestruct *p;
-       char   *s;
-       long    sum;
-       int     i;
-
-       crc_start();
-       for (p = save_array; p->address != NULL; p++)
-               sum = crc(p->address, p->width);
-       srandom((int) sum);
-
-       if ((out = fopen(outfile, "wb")) == NULL) {
-               fprintf(stderr,
-                   "Hmm.  The name \"%s\" appears to be magically blocked.\n",
-                   outfile);
-               return 1;
-       }
-       fwrite(&sum, sizeof(sum), 1, out);      /* Here's the random() key */
-       for (p = save_array; p->address != NULL; p++) {
-               for (s = p->address, i = 0; i < p->width; i++, s++)
-                       *s = (*s ^ random()) & 0xFF;    /* Lightly encrypt */
-               fwrite(p->address, p->width, 1, out);
-       }
-       fclose(out);
-       return 0;
-}
-
-int
-restore(infile)
-       const char   *infile;
+static int
+compat_restore(const char *infile)
 {
        FILE   *in;
-       struct savestruct *p;
+       const struct compat_saveinfo *p;
        char   *s;
        long    sum, cksum = 0;
-       int     i;
+       size_t  i;
+       struct crcstate crc;
 
        if ((in = fopen(infile, "rb")) == NULL) {
                fprintf(stderr,
@@ -174,19 +543,328 @@ restore(infile)
        }
        fread(&sum, sizeof(sum), 1, in);        /* Get the seed */
        srandom((int) sum);
-       for (p = save_array; p->address != NULL; p++) {
+       for (p = compat_savearray; p->address != NULL; p++) {
                fread(p->address, p->width, 1, in);
                for (s = p->address, i = 0; i < p->width; i++, s++)
                        *s = (*s ^ random()) & 0xFF;    /* Lightly decrypt */
        }
        fclose(in);
 
-       crc_start();            /* See if she cheated */
-       for (p = save_array; p->address != NULL; p++)
-               cksum = crc(p->address, p->width);
+       crc_start(&crc);                /* See if she cheated */
+       for (p = compat_savearray; p->address != NULL; p++)
+               crc_add(&crc, p->address, p->width);
+       cksum = crc_get(&crc);
        if (sum != cksum)       /* Tsk tsk */
                return 2;       /* Altered the file */
        /* We successfully restored, so this really was a save file */
-       /* Get rid of the file, but don't bother checking that we did */
+
+       /*
+        * The above code loads these from disk even though they're
+        * pointers. Null them out and hope we don't crash on them
+        * later; that's better than having them be garbage.
+        */
+       tkk = NULL;
+       wd1 = NULL;
+       wd2 = NULL;
+
+       return 0;
+}
+
+////////////////////////////////////////////////////////////
+// save + restore
+
+static int *const save_ints[] = {
+       &abbnum,
+       &attack,
+       &blklin,
+       &bonus,
+       &chloc,
+       &chloc2,
+       &clock1,
+       &clock2,
+       &closed,
+       &isclosing,
+       &daltloc,
+       &demo,
+       &detail,
+       &dflag,
+       &dkill,
+       &dtotal,
+       &foobar,
+       &gaveup,
+       &holding,
+       &iwest,
+       &k,
+       &k2,
+       &knfloc,
+       &kq,
+       &latency,
+       &limit,
+       &lmwarn,
+       &loc,
+       &maxdie,
+       &maxscore,
+       &newloc,
+       &numdie,
+       &obj,
+       &oldloc2,
+       &oldloc,
+       &panic,
+       &saveday,
+       &savet,
+       &scoring,
+       &spk,
+       &stick,
+       &tally,
+       &tally2,
+       &turns,
+       &verb,
+       &wasdark,
+       &yea,
+};
+static const unsigned num_save_ints = __arraycount(save_ints);
+
+#define INTARRAY(sym) { sym, __arraycount(sym) }
+
+static const struct {
+       int *ptr;
+       unsigned num;
+} save_intarrays[] = {
+       INTARRAY(atloc),
+       INTARRAY(dseen),
+       INTARRAY(dloc),
+       INTARRAY(odloc),
+       INTARRAY(fixed),
+       INTARRAY(hinted),
+       INTARRAY(links),
+       INTARRAY(place),
+       INTARRAY(prop),
+       INTARRAY(tk),
+};
+static const unsigned num_save_intarrays = __arraycount(save_intarrays);
+
+#undef INTARRAY
+
+#if 0
+static const struct {
+       void *ptr;
+       size_t len;
+} save_blobs[] = {
+       { &wd1, sizeof(wd1) },
+       { &wd2, sizeof(wd2) },
+       { &tkk, sizeof(tkk) },
+};
+static const unsigned num_save_blobs = __arraycount(save_blobs);
+#endif
+
+/*
+ * Write out a save file. Returns nonzero on error.
+ */
+int
+save(const char *outfile)
+{
+       struct savefile *sf;
+       struct timespec now;
+       uint32_t key, writeable_key;
+       uint32_t version;
+       unsigned i, j, n;
+       uint32_t val, sum;
+
+       sf = savefile_open(outfile, true);
+       if (sf == NULL) {
+               return 1;
+       }
+
+       if (savefile_rawwrite(sf, header, strlen(header))) {
+               savefile_close(sf);
+               return 1;
+       }
+
+       version = htonl(FORMAT_VERSION);
+       if (savefile_binwrite(sf, &version, sizeof(version))) {
+               savefile_close(sf);
+               return 1;
+       }
+
+       clock_gettime(CLOCK_REALTIME, &now);
+       key = (uint32_t)(now.tv_sec & 0xffffffff) ^ (uint32_t)(now.tv_nsec);
+
+       writeable_key = htonl(key);
+       if (savefile_binwrite(sf, &writeable_key, sizeof(writeable_key))) {
+               savefile_close(sf);
+               return 1;
+       }
+
+       /* other parts of the code may depend on us doing this here */
+       srandom(key);
+
+       savefile_key(sf, key);
+
+       /*
+        * Integers
+        */
+       for (i=0; i<num_save_ints; i++) {
+               val = *(save_ints[i]);
+               val = htonl(val);
+               if (savefile_cwrite(sf, &val, sizeof(val))) {
+                       savefile_close(sf);
+                       return 1;
+               }
+       }
+
+       /*
+        * Arrays of integers
+        */
+       for (i=0; i<num_save_intarrays; i++) {
+               n = save_intarrays[i].num;
+               for (j=0; j<n; j++) {
+                       val = save_intarrays[i].ptr[j];
+                       val = htonl(val);
+                       if (savefile_cwrite(sf, &val, sizeof(val))) {
+                               savefile_close(sf);
+                               return 1;
+                       }
+               }
+       }
+
+#if 0
+       /*
+        * Blobs
+        */
+       for (i=0; i<num_save_blobs; i++) {
+               if (savefile_cwrite(sf, save_blobs[i].ptr, save_blobs[i].len)) {
+                       savefile_close(sf);
+                       return 1;
+               }
+       }
+#endif
+
+       sum = htonl(crc_get(&sf->crc));
+       if (savefile_binwrite(sf, &sum, sizeof(&sum))) {
+               savefile_close(sf);
+               return 1;
+       }
+       savefile_close(sf);
+       return 0;
+}
+
+/*
+ * Read in a save file. Returns nonzero on error.
+ */
+int
+restore(const char *infile)
+{
+       struct savefile *sf;
+       char buf[sizeof(header)];
+       size_t headersize = strlen(header);
+       uint32_t version, key, sum;
+       unsigned i, j, n;
+       uint32_t val;
+       bool skipsum = false;
+
+       sf = savefile_open(infile, false);
+       if (sf == NULL) {
+               return 1;
+       }
+
+       if (savefile_rawread(sf, buf, headersize)) {
+               savefile_close(sf);
+               return 1;
+       }
+       buf[headersize] = 0;
+       if (strcmp(buf, header) != 0) {
+               savefile_close(sf);
+               fprintf(stderr, "Oh dear, that isn't one of my save files.\n");
+               fprintf(stderr,
+                   "Trying the Olde Waye; this myte notte Worke.\n");
+               return compat_restore(infile);
+       }
+
+       if (savefile_binread(sf, &version, sizeof(version))) {
+               savefile_close(sf);
+               return 1;
+       }
+       version = ntohl(version);
+       switch (version) {
+           case FORMAT_VERSION:
+               break;
+           case FORMAT_VERSION_NOSUM:
+               skipsum = true;
+               break;
+           default:
+               savefile_close(sf);
+               fprintf(stderr,
+                   "Oh dear, that file must be from the future. I don't know"
+                   " how to read it!\n");
+               return 1;
+       }
+
+       if (savefile_binread(sf, &key, sizeof(key))) {
+               savefile_close(sf);
+               return 1;
+       }
+       key = ntohl(key);
+       savefile_key(sf, key);
+
+       /* other parts of the code may depend on us doing this here */
+       srandom(key);
+
+       /*
+        * Integers
+        */
+       for (i=0; i<num_save_ints; i++) {
+               if (savefile_cread(sf, &val, sizeof(val))) {
+                       savefile_close(sf);
+                       return 1;
+               }
+               val = ntohl(val);
+               *(save_ints[i]) = val;
+       }
+
+       /*
+        * Arrays of integers
+        */
+       for (i=0; i<num_save_intarrays; i++) {
+               n = save_intarrays[i].num;
+               for (j=0; j<n; j++) {
+                       if (savefile_cread(sf, &val, sizeof(val))) {
+                               savefile_close(sf);
+                               return 1;
+                       }
+                       val = ntohl(val);
+                       save_intarrays[i].ptr[j] = val;
+               }
+       }
+
+#if 0
+       /*
+        * Blobs
+        */
+       for (i=0; i<num_save_blobs; i++) {
+               if (savefile_cread(sf, save_blobs[i].ptr, save_blobs[i].len)) {
+                       savefile_close(sf);
+                       return 1;
+               }
+       }
+#endif
+
+       if (savefile_binread(sf, &sum, sizeof(&sum))) {
+               savefile_close(sf);
+               return 1;
+       }
+       sum = ntohl(sum);
+       /* See if she cheated */
+       if (!skipsum && sum != crc_get(&sf->crc)) {
+               /* Tsk tsk, altered the file */
+               savefile_close(sf);
+               return 2;
+       }
+       savefile_close(sf);
+
+       /* Load theoretically invalidates these */
+       tkk = NULL;
+       wd1 = NULL;
+       wd2 = NULL;
+
        return 0;
 }