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