iwm: Fix S:N reporting in ifconfig(8)
[dragonfly.git] / games / adventure / save.c
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 }