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