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