Home | History | Annotate | Line # | Download | only in adventure
save.c revision 1.12
      1  1.12  dholland /*	$NetBSD: save.c,v 1.12 2012/01/07 22:23:16 dholland Exp $	*/
      2   1.2       cgd 
      3   1.1       jtc /*-
      4   1.1       jtc  * Copyright (c) 1991, 1993
      5   1.1       jtc  *	The Regents of the University of California.  All rights reserved.
      6   1.1       jtc  *
      7   1.1       jtc  * The game adventure was originally written in Fortran by Will Crowther
      8   1.1       jtc  * and Don Woods.  It was later translated to C and enhanced by Jim
      9   1.1       jtc  * Gillogly.  This code is derived from software contributed to Berkeley
     10   1.1       jtc  * by Jim Gillogly at The Rand Corporation.
     11   1.1       jtc  *
     12   1.1       jtc  * Redistribution and use in source and binary forms, with or without
     13   1.1       jtc  * modification, are permitted provided that the following conditions
     14   1.1       jtc  * are met:
     15   1.1       jtc  * 1. Redistributions of source code must retain the above copyright
     16   1.1       jtc  *    notice, this list of conditions and the following disclaimer.
     17   1.1       jtc  * 2. Redistributions in binary form must reproduce the above copyright
     18   1.1       jtc  *    notice, this list of conditions and the following disclaimer in the
     19   1.1       jtc  *    documentation and/or other materials provided with the distribution.
     20   1.8       agc  * 3. Neither the name of the University nor the names of its contributors
     21   1.1       jtc  *    may be used to endorse or promote products derived from this software
     22   1.1       jtc  *    without specific prior written permission.
     23   1.1       jtc  *
     24   1.1       jtc  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
     25   1.1       jtc  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     26   1.1       jtc  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     27   1.1       jtc  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
     28   1.1       jtc  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     29   1.1       jtc  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     30   1.1       jtc  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     31   1.1       jtc  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     32   1.1       jtc  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     33   1.1       jtc  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     34   1.1       jtc  * SUCH DAMAGE.
     35   1.1       jtc  */
     36   1.1       jtc 
     37   1.3  christos #include <sys/cdefs.h>
     38   1.1       jtc #ifndef lint
     39   1.2       cgd #if 0
     40   1.1       jtc static char sccsid[] = "@(#)save.c	8.1 (Berkeley) 5/31/93";
     41   1.2       cgd #else
     42  1.12  dholland __RCSID("$NetBSD: save.c,v 1.12 2012/01/07 22:23:16 dholland Exp $");
     43   1.2       cgd #endif
     44   1.4     lukem #endif				/* not lint */
     45   1.1       jtc 
     46  1.12  dholland #include <sys/types.h>
     47  1.12  dholland #include <sys/time.h>
     48  1.12  dholland #include <stdbool.h>
     49   1.1       jtc #include <stdio.h>
     50   1.3  christos #include <stdlib.h>
     51  1.12  dholland #include <err.h>
     52  1.12  dholland #include <assert.h>
     53  1.12  dholland 
     54   1.1       jtc #include "hdr.h"
     55   1.3  christos #include "extern.h"
     56   1.1       jtc 
     57  1.12  dholland struct savefile {
     58  1.12  dholland 	FILE *f;
     59  1.12  dholland 	const char *name;
     60  1.12  dholland 	bool warned;
     61  1.12  dholland 	unsigned bintextpos;
     62  1.12  dholland 	uint32_t key;
     63  1.12  dholland 	uint32_t sum;
     64  1.12  dholland 	unsigned char pad[8];
     65  1.12  dholland 	unsigned padpos;
     66  1.12  dholland };
     67  1.12  dholland 
     68  1.12  dholland #define BINTEXT_WIDTH 60
     69  1.12  dholland #define FORMAT_VERSION 1
     70  1.12  dholland static const char header[] = "Adventure save file\n";
     71  1.12  dholland 
     72  1.12  dholland ////////////////////////////////////////////////////////////
     73  1.12  dholland // base16 output encoding
     74  1.12  dholland 
     75  1.12  dholland /*
     76  1.12  dholland  * Map 16 plain values into 90 coded values and back.
     77  1.12  dholland  */
     78  1.12  dholland 
     79  1.12  dholland static const char coding[90] =
     80  1.12  dholland 	"Db.GOyT]7a6zpF(c*5H9oK~0[WVAg&kR)ml,2^q-1Y3v+"
     81  1.12  dholland 	"X/=JirZL$C>_N?:}B{dfnsxU<@MQ%8|P!4h`ESt;euwIj"
     82  1.12  dholland ;
     83  1.12  dholland 
     84  1.12  dholland static int
     85  1.12  dholland readletter(char letter, unsigned char *ret)
     86  1.12  dholland {
     87  1.12  dholland 	const char *s;
     88  1.12  dholland 
     89  1.12  dholland 	s = strchr(coding, letter);
     90  1.12  dholland 	if (s == NULL) {
     91  1.12  dholland 		return 1;
     92  1.12  dholland 	}
     93  1.12  dholland 	*ret = (s - coding) % 16;
     94  1.12  dholland 	return 0;
     95  1.12  dholland }
     96  1.12  dholland 
     97  1.12  dholland static char
     98  1.12  dholland writeletter(unsigned char nibble)
     99  1.12  dholland {
    100  1.12  dholland 	unsigned code;
    101  1.12  dholland 
    102  1.12  dholland 	assert(nibble < 16);
    103  1.12  dholland 	do {
    104  1.12  dholland 		code = (16 * (random() % 6)) + nibble;
    105  1.12  dholland 	} while (code >= 90);
    106  1.12  dholland 	return coding[code];
    107  1.12  dholland }
    108  1.12  dholland 
    109  1.12  dholland ////////////////////////////////////////////////////////////
    110  1.12  dholland // savefile
    111  1.12  dholland 
    112  1.12  dholland /*
    113  1.12  dholland  * Open a savefile.
    114  1.12  dholland  */
    115  1.12  dholland static struct savefile *
    116  1.12  dholland savefile_open(const char *name, bool forwrite)
    117  1.12  dholland {
    118  1.12  dholland 	struct savefile *sf;
    119  1.12  dholland 
    120  1.12  dholland 	sf = malloc(sizeof(*sf));
    121  1.12  dholland 	if (sf == NULL) {
    122  1.12  dholland 		return NULL;
    123  1.12  dholland 	}
    124  1.12  dholland 	sf->f = fopen(name, forwrite ? "w" : "r");
    125  1.12  dholland 	if (sf->f == NULL) {
    126  1.12  dholland 		free(sf);
    127  1.12  dholland 		fprintf(stderr,
    128  1.12  dholland 		    "Hmm.  The name \"%s\" appears to be magically blocked.\n",
    129  1.12  dholland 		    name);
    130  1.12  dholland 		return NULL;
    131  1.12  dholland 	}
    132  1.12  dholland 	sf->name = name;
    133  1.12  dholland 	sf->warned = false;
    134  1.12  dholland 	sf->bintextpos = 0;
    135  1.12  dholland 	sf->key = 0;
    136  1.12  dholland 	sf->sum = 0;
    137  1.12  dholland 	memset(sf->pad, 0, sizeof(sf->pad));
    138  1.12  dholland 	sf->padpos = 0;
    139  1.12  dholland 	return sf;
    140  1.12  dholland }
    141  1.12  dholland 
    142  1.12  dholland /*
    143  1.12  dholland  * Raw read.
    144  1.12  dholland  */
    145  1.12  dholland static int
    146  1.12  dholland savefile_rawread(struct savefile *sf, void *data, size_t len)
    147  1.12  dholland {
    148  1.12  dholland 	size_t result;
    149  1.12  dholland 
    150  1.12  dholland 	result = fread(data, 1, len, sf->f);
    151  1.12  dholland 	if (result != len || ferror(sf->f)) {
    152  1.12  dholland 		fprintf(stderr, "Oops: error reading %s.\n", sf->name);
    153  1.12  dholland 		sf->warned = true;
    154  1.12  dholland 		return 1;
    155  1.12  dholland 	}
    156  1.12  dholland 	return 0;
    157  1.12  dholland }
    158  1.12  dholland 
    159  1.12  dholland /*
    160  1.12  dholland  * Raw write.
    161  1.12  dholland  */
    162  1.12  dholland static int
    163  1.12  dholland savefile_rawwrite(struct savefile *sf, const void *data, size_t len)
    164  1.12  dholland {
    165  1.12  dholland 	size_t result;
    166  1.12  dholland 
    167  1.12  dholland 	result = fwrite(data, 1, len, sf->f);
    168  1.12  dholland 	if (result != len || ferror(sf->f)) {
    169  1.12  dholland 		fprintf(stderr, "Oops: error writing %s.\n", sf->name);
    170  1.12  dholland 		sf->warned = true;
    171  1.12  dholland 		return 1;
    172  1.12  dholland 	}
    173  1.12  dholland 	return 0;
    174  1.12  dholland }
    175  1.12  dholland 
    176  1.12  dholland /*
    177  1.12  dholland  * Close a savefile.
    178  1.12  dholland  */
    179  1.12  dholland static int
    180  1.12  dholland savefile_close(struct savefile *sf)
    181  1.12  dholland {
    182  1.12  dholland 	int ret;
    183  1.12  dholland 
    184  1.12  dholland 	if (sf->bintextpos > 0) {
    185  1.12  dholland 		savefile_rawwrite(sf, "\n", 1);
    186  1.12  dholland 	}
    187  1.12  dholland 
    188  1.12  dholland 	ret = 0;
    189  1.12  dholland 	if (fclose(sf->f)) {
    190  1.12  dholland 		if (!sf->warned) {
    191  1.12  dholland 			fprintf(stderr, "Oops: error on %s.\n", sf->name);
    192  1.12  dholland 		}
    193  1.12  dholland 		ret = 1;
    194  1.12  dholland 	}
    195  1.12  dholland 	free(sf);
    196  1.12  dholland 	return ret;
    197  1.12  dholland }
    198  1.12  dholland 
    199  1.12  dholland /*
    200  1.12  dholland  * Read encoded binary data, discarding any whitespace that appears.
    201  1.12  dholland  */
    202  1.12  dholland static int
    203  1.12  dholland savefile_bintextread(struct savefile *sf, void *data, size_t len)
    204  1.12  dholland {
    205  1.12  dholland 	size_t pos;
    206  1.12  dholland 	unsigned char *udata;
    207  1.12  dholland 	int ch;
    208  1.12  dholland 
    209  1.12  dholland 	udata = data;
    210  1.12  dholland 	pos = 0;
    211  1.12  dholland 	while (pos < len) {
    212  1.12  dholland 		ch = fgetc(sf->f);
    213  1.12  dholland 		if (ch == EOF || ferror(sf->f)) {
    214  1.12  dholland 			fprintf(stderr, "Oops: error reading %s.\n", sf->name);
    215  1.12  dholland 			sf->warned = true;
    216  1.12  dholland 			return 1;
    217  1.12  dholland 		}
    218  1.12  dholland 		if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') {
    219  1.12  dholland 			continue;
    220  1.12  dholland 		}
    221  1.12  dholland 		udata[pos++] = ch;
    222  1.12  dholland 	}
    223  1.12  dholland 	return 0;
    224  1.12  dholland }
    225  1.12  dholland 
    226  1.12  dholland /*
    227  1.12  dholland  * Read binary data, decoding from text using readletter().
    228  1.12  dholland  */
    229  1.12  dholland static int
    230  1.12  dholland savefile_binread(struct savefile *sf, void *data, size_t len)
    231  1.12  dholland {
    232  1.12  dholland 	unsigned char buf[64];
    233  1.12  dholland 	unsigned char *udata;
    234  1.12  dholland 	unsigned char val1, val2;
    235  1.12  dholland 	size_t pos, amt, i;
    236  1.12  dholland 
    237  1.12  dholland 	udata = data;
    238  1.12  dholland 	pos = 0;
    239  1.12  dholland 	while (pos < len) {
    240  1.12  dholland 		amt = len - pos;
    241  1.12  dholland 		if (amt > sizeof(buf) / 2) {
    242  1.12  dholland 			amt = sizeof(buf) / 2;
    243  1.12  dholland 		}
    244  1.12  dholland 		if (savefile_bintextread(sf, buf, amt*2)) {
    245  1.12  dholland 			return 1;
    246  1.12  dholland 		}
    247  1.12  dholland 		for (i=0; i<amt; i++) {
    248  1.12  dholland 			if (readletter(buf[i*2], &val1)) {
    249  1.12  dholland 				return 1;
    250  1.12  dholland 			}
    251  1.12  dholland 			if (readletter(buf[i*2 + 1], &val2)) {
    252  1.12  dholland 				return 1;
    253  1.12  dholland 			}
    254  1.12  dholland 			udata[pos++] = val1 * 16 + val2;
    255  1.12  dholland 		}
    256  1.12  dholland 	}
    257  1.12  dholland 	return 0;
    258  1.12  dholland }
    259  1.12  dholland 
    260  1.12  dholland /*
    261  1.12  dholland  * Write encoded binary data, inserting newlines to get a neatly
    262  1.12  dholland  * formatted block.
    263  1.12  dholland  */
    264  1.12  dholland static int
    265  1.12  dholland savefile_bintextwrite(struct savefile *sf, const void *data, size_t len)
    266  1.12  dholland {
    267  1.12  dholland 	size_t pos, amt;
    268  1.12  dholland 	const unsigned char *udata;
    269  1.12  dholland 
    270  1.12  dholland 	udata = data;
    271  1.12  dholland 	pos = 0;
    272  1.12  dholland 	while (pos < len) {
    273  1.12  dholland 		amt = BINTEXT_WIDTH - sf->bintextpos;
    274  1.12  dholland 		if (amt > len - pos) {
    275  1.12  dholland 			amt = len - pos;
    276  1.12  dholland 		}
    277  1.12  dholland 		if (savefile_rawwrite(sf, udata + pos, amt)) {
    278  1.12  dholland 			return 1;
    279  1.12  dholland 		}
    280  1.12  dholland 		pos += amt;
    281  1.12  dholland 		sf->bintextpos += amt;
    282  1.12  dholland 		if (sf->bintextpos >= BINTEXT_WIDTH) {
    283  1.12  dholland 			savefile_rawwrite(sf, "\n", 1);
    284  1.12  dholland 			sf->bintextpos = 0;
    285  1.12  dholland 		}
    286  1.12  dholland 	}
    287  1.12  dholland 	return 0;
    288  1.12  dholland }
    289  1.12  dholland 
    290  1.12  dholland /*
    291  1.12  dholland  * Write binary data, encoding as text using writeletter().
    292  1.12  dholland  */
    293  1.12  dholland static int
    294  1.12  dholland savefile_binwrite(struct savefile *sf, const void *data, size_t len)
    295  1.12  dholland {
    296  1.12  dholland 	unsigned char buf[64];
    297  1.12  dholland 	const unsigned char *udata;
    298  1.12  dholland 	size_t pos, bpos;
    299  1.12  dholland 	unsigned char byte;
    300  1.12  dholland 
    301  1.12  dholland 	udata = data;
    302  1.12  dholland 	pos = 0;
    303  1.12  dholland 	bpos = 0;
    304  1.12  dholland 	while (pos < len) {
    305  1.12  dholland 		byte = udata[pos++];
    306  1.12  dholland 		buf[bpos++] = writeletter(byte >> 4);
    307  1.12  dholland 		buf[bpos++] = writeletter(byte & 0xf);
    308  1.12  dholland 		if (bpos >= sizeof(buf)) {
    309  1.12  dholland 			if (savefile_bintextwrite(sf, buf, bpos)) {
    310  1.12  dholland 				return 1;
    311  1.12  dholland 			}
    312  1.12  dholland 			bpos = 0;
    313  1.12  dholland 		}
    314  1.12  dholland 	}
    315  1.12  dholland 	if (savefile_bintextwrite(sf, buf, bpos)) {
    316  1.12  dholland 		return 1;
    317  1.12  dholland 	}
    318  1.12  dholland 	return 0;
    319  1.12  dholland }
    320  1.12  dholland 
    321  1.12  dholland /*
    322  1.12  dholland  * Lightweight "encryption" for save files. This is not meant to
    323  1.12  dholland  * be secure and wouldn't be even if we didn't write the decrypt
    324  1.12  dholland  * key to the beginning of the save file; it's just meant to be
    325  1.12  dholland  * enough to discourage casual cheating.
    326  1.12  dholland  */
    327  1.12  dholland 
    328  1.12  dholland /*
    329  1.12  dholland  * Make cheesy hash of buf[0..buflen]. Note: buf and outhash may overlap.
    330  1.12  dholland  */
    331  1.12  dholland static void
    332  1.12  dholland hash(const void *data, size_t datalen, unsigned char *out, size_t outlen)
    333  1.12  dholland {
    334  1.12  dholland 	const unsigned char *udata;
    335  1.12  dholland 	size_t i;
    336  1.12  dholland 	uint64_t val;
    337  1.12  dholland 	const unsigned char *uval;
    338  1.12  dholland 	size_t valpos;
    339  1.12  dholland 
    340  1.12  dholland 	udata = data;
    341  1.12  dholland 	val = 0;
    342  1.12  dholland 	for (i=0; i<datalen; i++) {
    343  1.12  dholland 		val = val ^ 0xbadc0ffee;
    344  1.12  dholland 		val = (val << 4) | (val >> 60);
    345  1.12  dholland 		val += udata[i] ^ 0xbeef;
    346  1.12  dholland 	}
    347  1.12  dholland 
    348  1.12  dholland 	uval = (unsigned char *)&val;
    349  1.12  dholland 	valpos = 0;
    350  1.12  dholland 	for (i=0; i<outlen; i++) {
    351  1.12  dholland 		out[i] = uval[valpos++];
    352  1.12  dholland 		if (valpos >= sizeof(val)) {
    353  1.12  dholland 			valpos = 0;
    354  1.12  dholland 		}
    355  1.12  dholland 	}
    356  1.12  dholland }
    357  1.12  dholland 
    358  1.12  dholland /*
    359  1.12  dholland  * Set the "encryption" key.
    360  1.12  dholland  */
    361  1.12  dholland static void
    362  1.12  dholland savefile_key(struct savefile *sf, uint32_t key)
    363  1.12  dholland {
    364  1.12  dholland 	sf->key = 0;
    365  1.12  dholland 	sf->sum = 0;
    366  1.12  dholland 	hash(&sf->key, sizeof(sf->key), sf->pad, sizeof(sf->pad));
    367  1.12  dholland 	sf->padpos = 0;
    368  1.12  dholland }
    369  1.12  dholland 
    370  1.12  dholland /*
    371  1.12  dholland  * Get an "encryption" pad byte. This forms a stream "cipher" that we
    372  1.12  dholland  * xor with the plaintext save data.
    373  1.12  dholland  */
    374  1.12  dholland static unsigned char
    375  1.12  dholland savefile_getpad(struct savefile *sf)
    376  1.12  dholland {
    377  1.12  dholland 	unsigned char ret;
    378  1.12  dholland 
    379  1.12  dholland 	ret = sf->pad[sf->padpos++];
    380  1.12  dholland 	if (sf->padpos >= sizeof(sf->pad)) {
    381  1.12  dholland 		hash(sf->pad, sizeof(sf->pad), sf->pad, sizeof(sf->pad));
    382  1.12  dholland 		sf->padpos = 0;
    383  1.12  dholland 	}
    384  1.12  dholland 	return ret;
    385  1.12  dholland }
    386  1.12  dholland 
    387  1.12  dholland /*
    388  1.12  dholland  * Read "encrypted" data.
    389  1.12  dholland  */
    390  1.12  dholland static int
    391  1.12  dholland savefile_cread(struct savefile *sf, void *data, size_t len)
    392  1.12  dholland {
    393  1.12  dholland 	char buf[64];
    394  1.12  dholland 	unsigned char *udata;
    395  1.12  dholland 	size_t pos, amt, i;
    396  1.12  dholland 	unsigned char ch;
    397  1.12  dholland 
    398  1.12  dholland 	udata = data;
    399  1.12  dholland 	pos = 0;
    400  1.12  dholland 	while (pos < len) {
    401  1.12  dholland 		amt = len - pos;
    402  1.12  dholland 		if (amt > sizeof(buf)) {
    403  1.12  dholland 			amt = sizeof(buf);
    404  1.12  dholland 		}
    405  1.12  dholland 		if (savefile_binread(sf, buf, amt)) {
    406  1.12  dholland 			return 1;
    407  1.12  dholland 		}
    408  1.12  dholland 		for (i=0; i<amt; i++) {
    409  1.12  dholland 			ch = buf[i];
    410  1.12  dholland 			ch ^= savefile_getpad(sf);
    411  1.12  dholland 			udata[pos + i] = ch;
    412  1.12  dholland 		}
    413  1.12  dholland 		pos += amt;
    414  1.12  dholland 	}
    415  1.12  dholland 	return 0;
    416  1.12  dholland }
    417  1.12  dholland 
    418  1.12  dholland /*
    419  1.12  dholland  * Write "encrypted" data.
    420  1.12  dholland  */
    421  1.12  dholland static int
    422  1.12  dholland savefile_cwrite(struct savefile *sf, const void *data, size_t len)
    423  1.12  dholland {
    424  1.12  dholland 	char buf[64];
    425  1.12  dholland 	const unsigned char *udata;
    426  1.12  dholland 	size_t pos, amt, i;
    427  1.12  dholland 	unsigned char ch;
    428  1.12  dholland 
    429  1.12  dholland 	udata = data;
    430  1.12  dholland 	pos = 0;
    431  1.12  dholland 	while (pos < len) {
    432  1.12  dholland 		amt = len - pos;
    433  1.12  dholland 		if (amt > sizeof(buf)) {
    434  1.12  dholland 			amt = sizeof(buf);
    435  1.12  dholland 		}
    436  1.12  dholland 		for (i=0; i<amt; i++) {
    437  1.12  dholland 			ch = udata[pos + i];
    438  1.12  dholland 			ch ^= savefile_getpad(sf);
    439  1.12  dholland 			buf[i] = ch;
    440  1.12  dholland 		}
    441  1.12  dholland 		if (savefile_binwrite(sf, buf, amt)) {
    442  1.12  dholland 			return 1;
    443  1.12  dholland 		}
    444  1.12  dholland 		pos += amt;
    445  1.12  dholland 	}
    446  1.12  dholland 	return 0;
    447  1.12  dholland }
    448  1.12  dholland 
    449  1.12  dholland ////////////////////////////////////////////////////////////
    450  1.12  dholland // compat for old save files
    451  1.12  dholland 
    452  1.12  dholland struct compat_saveinfo {
    453   1.4     lukem 	void   *address;
    454   1.4     lukem 	int     width;
    455   1.1       jtc };
    456   1.1       jtc 
    457  1.12  dholland static const struct compat_saveinfo compat_savearray[] =
    458   1.1       jtc {
    459   1.4     lukem 	{&abbnum, sizeof(abbnum)},
    460   1.4     lukem 	{&attack, sizeof(attack)},
    461   1.4     lukem 	{&blklin, sizeof(blklin)},
    462   1.4     lukem 	{&bonus, sizeof(bonus)},
    463   1.4     lukem 	{&chloc, sizeof(chloc)},
    464   1.4     lukem 	{&chloc2, sizeof(chloc2)},
    465   1.4     lukem 	{&clock1, sizeof(clock1)},
    466   1.4     lukem 	{&clock2, sizeof(clock2)},
    467   1.4     lukem 	{&closed, sizeof(closed)},
    468  1.11  dholland 	{&isclosing, sizeof(isclosing)},
    469  1.11  dholland 	{&daltloc, sizeof(daltloc)},
    470   1.4     lukem 	{&demo, sizeof(demo)},
    471   1.4     lukem 	{&detail, sizeof(detail)},
    472   1.4     lukem 	{&dflag, sizeof(dflag)},
    473   1.4     lukem 	{&dkill, sizeof(dkill)},
    474   1.4     lukem 	{&dtotal, sizeof(dtotal)},
    475   1.4     lukem 	{&foobar, sizeof(foobar)},
    476   1.4     lukem 	{&gaveup, sizeof(gaveup)},
    477  1.11  dholland 	{&holding, sizeof(holding)},
    478   1.4     lukem 	{&iwest, sizeof(iwest)},
    479   1.4     lukem 	{&k, sizeof(k)},
    480   1.4     lukem 	{&k2, sizeof(k2)},
    481   1.4     lukem 	{&knfloc, sizeof(knfloc)},
    482   1.4     lukem 	{&kq, sizeof(kq)},
    483  1.11  dholland 	{&latency, sizeof(latency)},
    484   1.4     lukem 	{&limit, sizeof(limit)},
    485   1.4     lukem 	{&lmwarn, sizeof(lmwarn)},
    486   1.4     lukem 	{&loc, sizeof(loc)},
    487   1.4     lukem 	{&maxdie, sizeof(maxdie)},
    488  1.11  dholland 	{&maxscore, sizeof(maxscore)},
    489   1.4     lukem 	{&newloc, sizeof(newloc)},
    490   1.4     lukem 	{&numdie, sizeof(numdie)},
    491   1.4     lukem 	{&obj, sizeof(obj)},
    492  1.11  dholland 	{&oldloc2, sizeof(oldloc2)},
    493   1.4     lukem 	{&oldloc, sizeof(oldloc)},
    494   1.4     lukem 	{&panic, sizeof(panic)},
    495   1.6   hubertf 	{&saveday, sizeof(saveday)},
    496   1.4     lukem 	{&savet, sizeof(savet)},
    497  1.11  dholland 	{&scoring, sizeof(scoring)},
    498   1.4     lukem 	{&spk, sizeof(spk)},
    499   1.4     lukem 	{&stick, sizeof(stick)},
    500   1.4     lukem 	{&tally, sizeof(tally)},
    501   1.4     lukem 	{&tally2, sizeof(tally2)},
    502   1.4     lukem 	{&tkk, sizeof(tkk)},
    503   1.4     lukem 	{&turns, sizeof(turns)},
    504   1.4     lukem 	{&verb, sizeof(verb)},
    505   1.4     lukem 	{&wd1, sizeof(wd1)},
    506   1.4     lukem 	{&wd2, sizeof(wd2)},
    507  1.11  dholland 	{&wasdark, sizeof(wasdark)},
    508   1.4     lukem 	{&yea, sizeof(yea)},
    509   1.4     lukem 	{atloc, sizeof(atloc)},
    510   1.4     lukem 	{dloc, sizeof(dloc)},
    511   1.4     lukem 	{dseen, sizeof(dseen)},
    512   1.4     lukem 	{fixed, sizeof(fixed)},
    513   1.4     lukem 	{hinted, sizeof(hinted)},
    514   1.4     lukem 	{links, sizeof(links)},
    515   1.4     lukem 	{odloc, sizeof(odloc)},
    516   1.4     lukem 	{place, sizeof(place)},
    517   1.4     lukem 	{prop, sizeof(prop)},
    518   1.4     lukem 	{tk, sizeof(tk)},
    519   1.1       jtc 
    520   1.4     lukem 	{NULL, 0}
    521   1.1       jtc };
    522   1.1       jtc 
    523  1.12  dholland static int
    524  1.12  dholland compat_restore(const char *infile)
    525   1.1       jtc {
    526   1.4     lukem 	FILE   *in;
    527  1.12  dholland 	const struct compat_saveinfo *p;
    528   1.4     lukem 	char   *s;
    529   1.4     lukem 	long    sum, cksum = 0;
    530   1.4     lukem 	int     i;
    531   1.4     lukem 
    532   1.4     lukem 	if ((in = fopen(infile, "rb")) == NULL) {
    533   1.4     lukem 		fprintf(stderr,
    534   1.4     lukem 		    "Hmm.  The file \"%s\" appears to be magically blocked.\n",
    535   1.4     lukem 		    infile);
    536   1.4     lukem 		return 1;
    537   1.1       jtc 	}
    538   1.4     lukem 	fread(&sum, sizeof(sum), 1, in);	/* Get the seed */
    539   1.1       jtc 	srandom((int) sum);
    540  1.12  dholland 	for (p = compat_savearray; p->address != NULL; p++) {
    541   1.1       jtc 		fread(p->address, p->width, 1, in);
    542   1.1       jtc 		for (s = p->address, i = 0; i < p->width; i++, s++)
    543   1.4     lukem 			*s = (*s ^ random()) & 0xFF;	/* Lightly decrypt */
    544   1.1       jtc 	}
    545   1.1       jtc 	fclose(in);
    546   1.1       jtc 
    547   1.4     lukem 	crc_start();		/* See if she cheated */
    548  1.12  dholland 	for (p = compat_savearray; p->address != NULL; p++)
    549   1.1       jtc 		cksum = crc(p->address, p->width);
    550   1.4     lukem 	if (sum != cksum)	/* Tsk tsk */
    551   1.4     lukem 		return 2;	/* Altered the file */
    552   1.1       jtc 	/* We successfully restored, so this really was a save file */
    553  1.12  dholland 
    554  1.12  dholland 	/*
    555  1.12  dholland 	 * The above code loads these from disk even though they're
    556  1.12  dholland 	 * pointers. Null them out and hope we don't crash on them
    557  1.12  dholland 	 * later; that's better than having them be garbage.
    558  1.12  dholland 	 */
    559  1.12  dholland 	tkk = NULL;
    560  1.12  dholland 	wd1 = NULL;
    561  1.12  dholland 	wd2 = NULL;
    562  1.12  dholland 
    563  1.12  dholland 	return 0;
    564  1.12  dholland }
    565  1.12  dholland 
    566  1.12  dholland ////////////////////////////////////////////////////////////
    567  1.12  dholland // save + restore
    568  1.12  dholland 
    569  1.12  dholland static int *const save_ints[] = {
    570  1.12  dholland 	&abbnum,
    571  1.12  dholland 	&attack,
    572  1.12  dholland 	&blklin,
    573  1.12  dholland 	&bonus,
    574  1.12  dholland 	&chloc,
    575  1.12  dholland 	&chloc2,
    576  1.12  dholland 	&clock1,
    577  1.12  dholland 	&clock2,
    578  1.12  dholland 	&closed,
    579  1.12  dholland 	&isclosing,
    580  1.12  dholland 	&daltloc,
    581  1.12  dholland 	&demo,
    582  1.12  dholland 	&detail,
    583  1.12  dholland 	&dflag,
    584  1.12  dholland 	&dkill,
    585  1.12  dholland 	&dtotal,
    586  1.12  dholland 	&foobar,
    587  1.12  dholland 	&gaveup,
    588  1.12  dholland 	&holding,
    589  1.12  dholland 	&iwest,
    590  1.12  dholland 	&k,
    591  1.12  dholland 	&k2,
    592  1.12  dholland 	&knfloc,
    593  1.12  dholland 	&kq,
    594  1.12  dholland 	&latency,
    595  1.12  dholland 	&limit,
    596  1.12  dholland 	&lmwarn,
    597  1.12  dholland 	&loc,
    598  1.12  dholland 	&maxdie,
    599  1.12  dholland 	&maxscore,
    600  1.12  dholland 	&newloc,
    601  1.12  dholland 	&numdie,
    602  1.12  dholland 	&obj,
    603  1.12  dholland 	&oldloc2,
    604  1.12  dholland 	&oldloc,
    605  1.12  dholland 	&panic,
    606  1.12  dholland 	&saveday,
    607  1.12  dholland 	&savet,
    608  1.12  dholland 	&scoring,
    609  1.12  dholland 	&spk,
    610  1.12  dholland 	&stick,
    611  1.12  dholland 	&tally,
    612  1.12  dholland 	&tally2,
    613  1.12  dholland 	&turns,
    614  1.12  dholland 	&verb,
    615  1.12  dholland 	&wasdark,
    616  1.12  dholland 	&yea,
    617  1.12  dholland };
    618  1.12  dholland static const unsigned num_save_ints = __arraycount(save_ints);
    619  1.12  dholland 
    620  1.12  dholland #define INTARRAY(sym) { sym, __arraycount(sym) }
    621  1.12  dholland 
    622  1.12  dholland static const struct {
    623  1.12  dholland 	int *ptr;
    624  1.12  dholland 	unsigned num;
    625  1.12  dholland } save_intarrays[] = {
    626  1.12  dholland 	INTARRAY(atloc),
    627  1.12  dholland 	INTARRAY(dseen),
    628  1.12  dholland 	INTARRAY(dloc),
    629  1.12  dholland 	INTARRAY(odloc),
    630  1.12  dholland 	INTARRAY(fixed),
    631  1.12  dholland 	INTARRAY(hinted),
    632  1.12  dholland 	INTARRAY(links),
    633  1.12  dholland 	INTARRAY(place),
    634  1.12  dholland 	INTARRAY(prop),
    635  1.12  dholland 	INTARRAY(tk),
    636  1.12  dholland };
    637  1.12  dholland static const unsigned num_save_intarrays = __arraycount(save_intarrays);
    638  1.12  dholland 
    639  1.12  dholland #undef INTARRAY
    640  1.12  dholland 
    641  1.12  dholland #if 0
    642  1.12  dholland static const struct {
    643  1.12  dholland 	void *ptr;
    644  1.12  dholland 	size_t len;
    645  1.12  dholland } save_blobs[] = {
    646  1.12  dholland 	{ &wd1, sizeof(wd1) },
    647  1.12  dholland 	{ &wd2, sizeof(wd2) },
    648  1.12  dholland 	{ &tkk, sizeof(tkk) },
    649  1.12  dholland };
    650  1.12  dholland static const unsigned num_save_blobs = __arraycount(save_blobs);
    651  1.12  dholland #endif
    652  1.12  dholland 
    653  1.12  dholland /*
    654  1.12  dholland  * Write out a save file. Returns nonzero on error.
    655  1.12  dholland  */
    656  1.12  dholland int
    657  1.12  dholland save(const char *outfile)
    658  1.12  dholland {
    659  1.12  dholland 	struct savefile *sf;
    660  1.12  dholland 	struct timespec now;
    661  1.12  dholland 	uint32_t key, writeable_key;
    662  1.12  dholland 	uint32_t version;
    663  1.12  dholland 	unsigned i, j, n;
    664  1.12  dholland 	uint32_t val;
    665  1.12  dholland 
    666  1.12  dholland 	sf = savefile_open(outfile, true);
    667  1.12  dholland 	if (sf == NULL) {
    668  1.12  dholland 		return 1;
    669  1.12  dholland 	}
    670  1.12  dholland 
    671  1.12  dholland 	if (savefile_rawwrite(sf, header, strlen(header))) {
    672  1.12  dholland 		savefile_close(sf);
    673  1.12  dholland 		return 1;
    674  1.12  dholland 	}
    675  1.12  dholland 
    676  1.12  dholland 	version = htonl(FORMAT_VERSION);
    677  1.12  dholland 	if (savefile_binwrite(sf, &version, sizeof(version))) {
    678  1.12  dholland 		savefile_close(sf);
    679  1.12  dholland 		return 1;
    680  1.12  dholland 	}
    681  1.12  dholland 
    682  1.12  dholland 	clock_gettime(CLOCK_REALTIME, &now);
    683  1.12  dholland 	key = (uint32_t)(now.tv_sec & 0xffffffff) ^ (uint32_t)(now.tv_nsec);
    684  1.12  dholland 
    685  1.12  dholland 	writeable_key = htonl(key);
    686  1.12  dholland 	if (savefile_binwrite(sf, &writeable_key, sizeof(writeable_key))) {
    687  1.12  dholland 		savefile_close(sf);
    688  1.12  dholland 		return 1;
    689  1.12  dholland 	}
    690  1.12  dholland 
    691  1.12  dholland 	/* other parts of the code may depend on us doing this here */
    692  1.12  dholland 	srandom(key);
    693  1.12  dholland 
    694  1.12  dholland 	savefile_key(sf, key);
    695  1.12  dholland 
    696  1.12  dholland 	/*
    697  1.12  dholland 	 * Integers
    698  1.12  dholland 	 */
    699  1.12  dholland 	for (i=0; i<num_save_ints; i++) {
    700  1.12  dholland 		val = *(save_ints[i]);
    701  1.12  dholland 		val = htonl(val);
    702  1.12  dholland 		if (savefile_cwrite(sf, &val, sizeof(val))) {
    703  1.12  dholland 			savefile_close(sf);
    704  1.12  dholland 			return 1;
    705  1.12  dholland 		}
    706  1.12  dholland 	}
    707  1.12  dholland 
    708  1.12  dholland 	/*
    709  1.12  dholland 	 * Arrays of integers
    710  1.12  dholland 	 */
    711  1.12  dholland 	for (i=0; i<num_save_intarrays; i++) {
    712  1.12  dholland 		n = save_intarrays[i].num;
    713  1.12  dholland 		for (j=0; j<n; j++) {
    714  1.12  dholland 			val = save_intarrays[i].ptr[j];
    715  1.12  dholland 			val = htonl(val);
    716  1.12  dholland 			if (savefile_cwrite(sf, &val, sizeof(val))) {
    717  1.12  dholland 				savefile_close(sf);
    718  1.12  dholland 				return 1;
    719  1.12  dholland 			}
    720  1.12  dholland 		}
    721  1.12  dholland 	}
    722  1.12  dholland 
    723  1.12  dholland #if 0
    724  1.12  dholland 	/*
    725  1.12  dholland 	 * Blobs
    726  1.12  dholland 	 */
    727  1.12  dholland 	for (i=0; i<num_save_blobs; i++) {
    728  1.12  dholland 		if (savefile_cwrite(sf, save_blobs[i].ptr, save_blobs[i].len)) {
    729  1.12  dholland 			savefile_close(sf);
    730  1.12  dholland 			return 1;
    731  1.12  dholland 		}
    732  1.12  dholland 	}
    733  1.12  dholland #endif
    734  1.12  dholland 
    735  1.12  dholland 	sf->sum = htonl(sf->sum);
    736  1.12  dholland 	if (savefile_binwrite(sf, &sf->sum, sizeof(&sf->sum))) {
    737  1.12  dholland 		savefile_close(sf);
    738  1.12  dholland 		return 1;
    739  1.12  dholland 	}
    740  1.12  dholland 	savefile_close(sf);
    741  1.12  dholland 	return 0;
    742  1.12  dholland }
    743  1.12  dholland 
    744  1.12  dholland /*
    745  1.12  dholland  * Read in a save file. Returns nonzero on error.
    746  1.12  dholland  */
    747  1.12  dholland int
    748  1.12  dholland restore(const char *infile)
    749  1.12  dholland {
    750  1.12  dholland 	struct savefile *sf;
    751  1.12  dholland 	char buf[sizeof(header)];
    752  1.12  dholland 	size_t headersize = strlen(header);
    753  1.12  dholland 	uint32_t version, key, sum;
    754  1.12  dholland 	unsigned i, j, n;
    755  1.12  dholland 	uint32_t val;
    756  1.12  dholland 
    757  1.12  dholland 	sf = savefile_open(infile, false);
    758  1.12  dholland 	if (sf == NULL) {
    759  1.12  dholland 		return 1;
    760  1.12  dholland 	}
    761  1.12  dholland 
    762  1.12  dholland 	if (savefile_rawread(sf, buf, headersize)) {
    763  1.12  dholland 		savefile_close(sf);
    764  1.12  dholland 		return 1;
    765  1.12  dholland 	}
    766  1.12  dholland 	buf[headersize] = 0;
    767  1.12  dholland 	if (strcmp(buf, header) != 0) {
    768  1.12  dholland 		savefile_close(sf);
    769  1.12  dholland 		fprintf(stderr, "Oh dear, that isn't one of my save files.\n");
    770  1.12  dholland 		fprintf(stderr,
    771  1.12  dholland 		    "Trying the Olde Waye; this myte notte Worke.\n");
    772  1.12  dholland 		return compat_restore(infile);
    773  1.12  dholland 	}
    774  1.12  dholland 
    775  1.12  dholland 	if (savefile_binread(sf, &version, sizeof(version))) {
    776  1.12  dholland 		savefile_close(sf);
    777  1.12  dholland 		return 1;
    778  1.12  dholland 	}
    779  1.12  dholland 	version = ntohl(version);
    780  1.12  dholland 	if (version != FORMAT_VERSION) {
    781  1.12  dholland 		savefile_close(sf);
    782  1.12  dholland 		fprintf(stderr,
    783  1.12  dholland 		    "Oh dear, that file must be from the future. I don't know"
    784  1.12  dholland 		    " how to read it!\n");
    785  1.12  dholland 		return 1;
    786  1.12  dholland 	}
    787  1.12  dholland 
    788  1.12  dholland 	if (savefile_binread(sf, &key, sizeof(key))) {
    789  1.12  dholland 		savefile_close(sf);
    790  1.12  dholland 		return 1;
    791  1.12  dholland 	}
    792  1.12  dholland 	key = ntohl(key);
    793  1.12  dholland 	savefile_key(sf, key);
    794  1.12  dholland 
    795  1.12  dholland 	/* other parts of the code may depend on us doing this here */
    796  1.12  dholland 	srandom(key);
    797  1.12  dholland 
    798  1.12  dholland 	/*
    799  1.12  dholland 	 * Integers
    800  1.12  dholland 	 */
    801  1.12  dholland 	for (i=0; i<num_save_ints; i++) {
    802  1.12  dholland 		if (savefile_cread(sf, &val, sizeof(val))) {
    803  1.12  dholland 			savefile_close(sf);
    804  1.12  dholland 			return 1;
    805  1.12  dholland 		}
    806  1.12  dholland 		val = ntohl(val);
    807  1.12  dholland 		*(save_ints[i]) = val;
    808  1.12  dholland 	}
    809  1.12  dholland 
    810  1.12  dholland 	/*
    811  1.12  dholland 	 * Arrays of integers
    812  1.12  dholland 	 */
    813  1.12  dholland 	for (i=0; i<num_save_intarrays; i++) {
    814  1.12  dholland 		n = save_intarrays[i].num;
    815  1.12  dholland 		for (j=0; j<n; j++) {
    816  1.12  dholland 			if (savefile_cread(sf, &val, sizeof(val))) {
    817  1.12  dholland 				savefile_close(sf);
    818  1.12  dholland 				return 1;
    819  1.12  dholland 			}
    820  1.12  dholland 			val = ntohl(val);
    821  1.12  dholland 			save_intarrays[i].ptr[j] = val;
    822  1.12  dholland 		}
    823  1.12  dholland 	}
    824  1.12  dholland 
    825  1.12  dholland #if 0
    826  1.12  dholland 	/*
    827  1.12  dholland 	 * Blobs
    828  1.12  dholland 	 */
    829  1.12  dholland 	for (i=0; i<num_save_blobs; i++) {
    830  1.12  dholland 		if (savefile_cread(sf, save_blobs[i].ptr, save_blobs[i].len)) {
    831  1.12  dholland 			savefile_close(sf);
    832  1.12  dholland 			return 1;
    833  1.12  dholland 		}
    834  1.12  dholland 	}
    835  1.12  dholland #endif
    836  1.12  dholland 
    837  1.12  dholland 	if (savefile_binread(sf, &sum, sizeof(&sum))) {
    838  1.12  dholland 		savefile_close(sf);
    839  1.12  dholland 		return 1;
    840  1.12  dholland 	}
    841  1.12  dholland 	sum = ntohl(sum);
    842  1.12  dholland 	/* See if she cheated */
    843  1.12  dholland 	if (sum != sf->sum) {
    844  1.12  dholland 		/* Tsk tsk, altered the file */
    845  1.12  dholland 		savefile_close(sf);
    846  1.12  dholland 		return 2;
    847  1.12  dholland 	}
    848  1.12  dholland 	savefile_close(sf);
    849  1.12  dholland 
    850  1.12  dholland 	/* Load theoretically invalidates these */
    851  1.12  dholland 	tkk = NULL;
    852  1.12  dholland 	wd1 = NULL;
    853  1.12  dholland 	wd2 = NULL;
    854  1.12  dholland 
    855   1.1       jtc 	return 0;
    856   1.1       jtc }
    857