Source-Changes-HG archive

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index][Old Index]

[src/trunk]: src/games/adventure Redo save file handling. The old save files ...



details:   https://anonhg.NetBSD.org/src/rev/bcb2f933d2d6
branches:  trunk
changeset: 772554:bcb2f933d2d6
user:      dholland <dholland%NetBSD.org@localhost>
date:      Sat Jan 07 22:23:16 2012 +0000

description:
Redo save file handling. The old save files were unportable, had no
magic number or versioning, relied on random(3) never changing to a
different implementation, and were also saving pointers to disk and
reading them back again. It *looks* as if the pointers thus loaded
were reset before being used, but it's not particularly clear as the
main loop of this thing is goto-based FORTRAN translated lightly to C.
I've changed the logic to null these pointers instead of saving and
loading them, and things seem to still work.

The new save files have a header, support versioning, write only sized
types in network byte order, and for the toy encryption to discourage
cheating do something self-contained instead of using random(3) as a
stream cipher.

Because between the original import from 4.4 until earlier today
trying to save would result in SIGSEGV on most platforms, it's
unlikely anyone has a save file, but just in case (since the pointer
issue appears to be nonlethal) I've kept compat code for old save
files.

diffstat:

 games/adventure/save.c |  758 ++++++++++++++++++++++++++++++++++++++++++++++--
 1 files changed, 712 insertions(+), 46 deletions(-)

diffs (truncated from 810 to 300 lines):

diff -r 21a035e3dd02 -r bcb2f933d2d6 games/adventure/save.c
--- a/games/adventure/save.c    Sat Jan 07 21:03:05 2012 +0000
+++ b/games/adventure/save.c    Sat Jan 07 22:23:16 2012 +0000
@@ -1,4 +1,4 @@
-/*     $NetBSD: save.c,v 1.11 2009/08/25 06:56:52 dholland Exp $       */
+/*     $NetBSD: save.c,v 1.12 2012/01/07 22:23:16 dholland Exp $       */
 
 /*-
  * Copyright (c) 1991, 1993
@@ -39,22 +39,422 @@
 #if 0
 static char sccsid[] = "@(#)save.c     8.1 (Berkeley) 5/31/93";
 #else
-__RCSID("$NetBSD: save.c,v 1.11 2009/08/25 06:56:52 dholland Exp $");
+__RCSID("$NetBSD: save.c,v 1.12 2012/01/07 22:23:16 dholland Exp $");
 #endif
 #endif                         /* not lint */
 
-#include <err.h>
+#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 {
+struct savefile {
+       FILE *f;
+       const char *name;
+       bool warned;
+       unsigned bintextpos;
+       uint32_t key;
+       uint32_t sum;
+       unsigned char pad[8];
+       unsigned padpos;
+};
+
+#define BINTEXT_WIDTH 60
+#define FORMAT_VERSION 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;
+       sf->sum = 0;
+       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.



Home | Main Index | Thread Index | Old Index