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