xref: /netbsd-src/games/adventure/save.c (revision 18bae9314b0a39b981f2aa3abc84c80eab31e348)
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
readletter(char letter,unsigned char * ret)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
writeletter(unsigned char nibble)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 *
savefile_open(const char * name,bool forwrite)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
savefile_rawread(struct savefile * sf,void * data,size_t len)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
savefile_rawwrite(struct savefile * sf,const void * data,size_t len)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
savefile_close(struct savefile * sf)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
savefile_bintextread(struct savefile * sf,void * data,size_t len)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
savefile_binread(struct savefile * sf,void * data,size_t len)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
savefile_bintextwrite(struct savefile * sf,const void * data,size_t len)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
savefile_binwrite(struct savefile * sf,const void * data,size_t len)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
hash(const void * data,size_t datalen,unsigned char * out,size_t outlen)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
savefile_key(struct savefile * sf,uint32_t key)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
savefile_getpad(struct savefile * sf)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
savefile_cread(struct savefile * sf,void * data,size_t len)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
savefile_cwrite(struct savefile * sf,const void * data,size_t len)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
compat_restore(const char * infile)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
save(const char * outfile)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
restore(const char * infile)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