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