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