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