1 /* @(#)save.c 8.1 (Berkeley) 5/31/93 */
2 /* $NetBSD: save.c,v 1.14 2014/03/22 22:04:40 dholland Exp $ */
5 * Copyright (c) 1991, 1993
6 * The Regents of the University of California. All rights reserved.
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.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
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.
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
38 #include <sys/types.h>
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";
65 ////////////////////////////////////////////////////////////
66 // base16 output encoding
69 * Map 16 plain values into 90 coded values and back.
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"
78 readletter(char letter, unsigned char *ret)
82 s = strchr(coding, letter);
86 *ret = (s - coding) % 16;
91 writeletter(unsigned char nibble)
97 code = (16 * (random() % 6)) + nibble;
102 ////////////////////////////////////////////////////////////
108 static struct savefile *
109 savefile_open(const char *name, bool forwrite)
113 sf = malloc(sizeof(*sf));
117 sf->f = fopen(name, forwrite ? "w" : "r");
121 "Hmm. The name \"%s\" appears to be magically blocked.\n",
130 memset(sf->pad, 0, sizeof(sf->pad));
139 savefile_rawread(struct savefile *sf, void *data, size_t len)
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);
156 savefile_rawwrite(struct savefile *sf, const void *data, size_t len)
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);
173 savefile_close(struct savefile *sf)
177 if (sf->bintextpos > 0) {
178 savefile_rawwrite(sf, "\n", 1);
184 fprintf(stderr, "Oops: error on %s.\n", sf->name);
193 * Read encoded binary data, discarding any whitespace that appears.
196 savefile_bintextread(struct savefile *sf, void *data, size_t len)
199 unsigned char *udata;
206 if (ch == EOF || ferror(sf->f)) {
207 fprintf(stderr, "Oops: error reading %s.\n", sf->name);
211 if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') {
220 * Read binary data, decoding from text using readletter().
223 savefile_binread(struct savefile *sf, void *data, size_t len)
225 unsigned char buf[64];
226 unsigned char *udata;
227 unsigned char val1, val2;
234 if (amt > sizeof(buf) / 2) {
235 amt = sizeof(buf) / 2;
237 if (savefile_bintextread(sf, buf, amt*2)) {
240 for (i=0; i<amt; i++) {
241 if (readletter(buf[i*2], &val1)) {
244 if (readletter(buf[i*2 + 1], &val2)) {
247 udata[pos++] = val1 * 16 + val2;
254 * Write encoded binary data, inserting newlines to get a neatly
258 savefile_bintextwrite(struct savefile *sf, const void *data, size_t len)
261 const unsigned char *udata;
266 amt = BINTEXT_WIDTH - sf->bintextpos;
267 if (amt > len - pos) {
270 if (savefile_rawwrite(sf, udata + pos, amt)) {
274 sf->bintextpos += amt;
275 if (sf->bintextpos >= BINTEXT_WIDTH) {
276 savefile_rawwrite(sf, "\n", 1);
284 * Write binary data, encoding as text using writeletter().
287 savefile_binwrite(struct savefile *sf, const void *data, size_t len)
289 unsigned char buf[64];
290 const unsigned char *udata;
299 buf[bpos++] = writeletter(byte >> 4);
300 buf[bpos++] = writeletter(byte & 0xf);
301 if (bpos >= sizeof(buf)) {
302 if (savefile_bintextwrite(sf, buf, bpos)) {
308 if (savefile_bintextwrite(sf, buf, bpos)) {
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.
322 * Make cheesy hash of buf[0..buflen]. Note: buf and outhash may overlap.
325 hash(const void *data, size_t datalen, unsigned char *out, size_t outlen)
327 const unsigned char *udata;
330 const unsigned char *uval;
335 for (i=0; i<datalen; i++) {
336 val = val ^ 0xbadc0ffee;
337 val = (val << 4) | (val >> 60);
338 val += udata[i] ^ 0xbeefU;
341 uval = (unsigned char *)&val;
343 for (i=0; i<outlen; i++) {
344 out[i] = uval[valpos++];
345 if (valpos >= sizeof(val)) {
352 * Set the "encryption" key.
355 savefile_key(struct savefile *sf, __unused uint32_t key)
359 hash(&sf->key, sizeof(sf->key), sf->pad, sizeof(sf->pad));
364 * Get an "encryption" pad byte. This forms a stream "cipher" that we
365 * xor with the plaintext save data.
368 savefile_getpad(struct savefile *sf)
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));
381 * Read "encrypted" data.
384 savefile_cread(struct savefile *sf, void *data, size_t len)
387 unsigned char *udata;
395 if (amt > sizeof(buf)) {
398 if (savefile_binread(sf, buf, amt)) {
401 for (i=0; i<amt; i++) {
403 ch ^= savefile_getpad(sf);
408 crc_add(&sf->crc, data, len);
413 * Write "encrypted" data.
416 savefile_cwrite(struct savefile *sf, const void *data, size_t len)
419 const unsigned char *udata;
427 if (amt > sizeof(buf)) {
430 for (i=0; i<amt; i++) {
432 ch ^= savefile_getpad(sf);
435 if (savefile_binwrite(sf, buf, amt)) {
440 crc_add(&sf->crc, data, len);
444 ////////////////////////////////////////////////////////////
445 // compat for old save files
447 struct compat_saveinfo {
452 static const struct compat_saveinfo compat_savearray[] =
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)},
476 {&knfloc, sizeof(knfloc)},
478 {&latency, sizeof(latency)},
479 {&limit, sizeof(limit)},
480 {&lmwarn, sizeof(lmwarn)},
482 {&maxdie, sizeof(maxdie)},
483 {&maxscore, sizeof(maxscore)},
484 {&newloc, sizeof(newloc)},
485 {&numdie, sizeof(numdie)},
487 {&oldloc2, sizeof(oldloc2)},
488 {&oldloc, sizeof(oldloc)},
489 {&panic, sizeof(panic)},
490 {&saveday, sizeof(saveday)},
491 {&savet, sizeof(savet)},
492 {&scoring, sizeof(scoring)},
494 {&stick, sizeof(stick)},
495 {&tally, sizeof(tally)},
496 {&tally2, sizeof(tally2)},
498 {&turns, sizeof(turns)},
499 {&verb, sizeof(verb)},
502 {&wasdark, sizeof(wasdark)},
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)},
519 compat_restore(const char *infile)
522 const struct compat_saveinfo *p;
528 if ((in = fopen(infile, "rb")) == NULL) {
530 "Hmm. The file \"%s\" appears to be magically blocked.\n",
534 fread(&sum, sizeof(sum), 1, in); /* Get the seed */
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 */
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 */
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.
563 ////////////////////////////////////////////////////////////
566 static int *const save_ints[] = {
615 static const unsigned num_save_ints = __arraycount(save_ints);
617 #define INTARRAY(sym) { sym, __arraycount(sym) }
619 static const struct {
622 } save_intarrays[] = {
634 static const unsigned num_save_intarrays = __arraycount(save_intarrays);
639 static const struct {
643 { &wd1, sizeof(wd1) },
644 { &wd2, sizeof(wd2) },
645 { &tkk, sizeof(tkk) },
647 static const unsigned num_save_blobs = __arraycount(save_blobs);
651 * Write out a save file. Returns nonzero on error.
654 save(const char *outfile)
658 uint32_t key, writeable_key;
663 sf = savefile_open(outfile, true);
668 if (savefile_rawwrite(sf, header, strlen(header))) {
673 version = htonl(FORMAT_VERSION);
674 if (savefile_binwrite(sf, &version, sizeof(version))) {
679 clock_gettime(CLOCK_REALTIME, &now);
680 key = (uint32_t)(now.tv_sec & 0xffffffff) ^ (uint32_t)(now.tv_nsec);
682 writeable_key = htonl(key);
683 if (savefile_binwrite(sf, &writeable_key, sizeof(writeable_key))) {
688 /* other parts of the code may depend on us doing this here */
691 savefile_key(sf, key);
696 for (i=0; i<num_save_ints; i++) {
697 val = *(save_ints[i]);
699 if (savefile_cwrite(sf, &val, sizeof(val))) {
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];
713 if (savefile_cwrite(sf, &val, sizeof(val))) {
724 for (i=0; i<num_save_blobs; i++) {
725 if (savefile_cwrite(sf, save_blobs[i].ptr, save_blobs[i].len)) {
732 sum = htonl(crc_get(&sf->crc));
733 if (savefile_binwrite(sf, &sum, sizeof(&sum))) {
742 * Read in a save file. Returns nonzero on error.
745 restore(const char *infile)
748 char buf[sizeof(header)];
749 size_t headersize = strlen(header);
750 uint32_t version, key, sum;
753 bool skipsum = false;
755 sf = savefile_open(infile, false);
760 if (savefile_rawread(sf, buf, headersize)) {
765 if (strcmp(buf, header) != 0) {
767 fprintf(stderr, "Oh dear, that isn't one of my save files.\n");
769 "Trying the Olde Waye; this myte notte Worke.\n");
770 return compat_restore(infile);
773 if (savefile_binread(sf, &version, sizeof(version))) {
777 version = ntohl(version);
781 case FORMAT_VERSION_NOSUM:
787 "Oh dear, that file must be from the future. I don't know"
788 " how to read it!\n");
792 if (savefile_binread(sf, &key, sizeof(key))) {
797 savefile_key(sf, key);
799 /* other parts of the code may depend on us doing this here */
805 for (i=0; i<num_save_ints; i++) {
806 if (savefile_cread(sf, &val, sizeof(val))) {
811 *(save_ints[i]) = val;
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))) {
825 save_intarrays[i].ptr[j] = val;
833 for (i=0; i<num_save_blobs; i++) {
834 if (savefile_cread(sf, save_blobs[i].ptr, save_blobs[i].len)) {
841 if (savefile_binread(sf, &sum, sizeof(&sum))) {
846 /* See if she cheated */
847 if (!skipsum && sum != crc_get(&sf->crc)) {
848 /* Tsk tsk, altered the file */
854 /* Load theoretically invalidates these */