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