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
40static 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
57struct 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
71static 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
80static 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
85static int
86readletter(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
98static char
99writeletter(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 */
116static struct savefile *
117savefile_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 */
146static int
147savefile_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 */
163static int
164savefile_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 */
180static int
181savefile_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 */
203static int
204savefile_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 */
230static int
231savefile_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 */
265static int
266savefile_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 */
294static int
295savefile_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 */
332static void
333hash(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 */
362static void
363savefile_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 */
375static unsigned char
376savefile_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 */
391static int
392savefile_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 */
423static int
424savefile_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
455struct compat_saveinfo {
456	void   *address;
457	size_t  width;
458};
459
460static 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
526static int
527compat_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
574static 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};
623static const unsigned num_save_ints = __arraycount(save_ints);
624
625#define INTARRAY(sym) { sym, __arraycount(sym) }
626
627static 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};
642static const unsigned num_save_intarrays = __arraycount(save_intarrays);
643
644#undef INTARRAY
645
646#if 0
647static 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};
655static const unsigned num_save_blobs = __arraycount(save_blobs);
656#endif
657
658/*
659 * Write out a save file. Returns nonzero on error.
660 */
661int
662save(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 */
752int
753restore(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