1 /* $NetBSD: save.c,v 1.14 2014/03/22 22:04:40 dholland Exp $ */
4 * Copyright (c) 1991, 1993
5 * The Regents of the University of California. All rights reserved.
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.
12 * Redistribution and use in source and binary forms, with or without
13 * modification, are permitted provided that the following conditions
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.
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
37 #include <sys/cdefs.h>
40 static char sccsid
[] = "@(#)save.c 8.1 (Berkeley) 5/31/93";
42 __RCSID("$NetBSD: save.c,v 1.14 2014/03/22 22:04:40 dholland Exp $");
46 #include <sys/types.h>
68 #define BINTEXT_WIDTH 60
69 #define FORMAT_VERSION 2
70 #define FORMAT_VERSION_NOSUM 1
71 static const char header
[] = "Adventure save file\n";
73 ////////////////////////////////////////////////////////////
74 // base16 output encoding
77 * Map 16 plain values into 90 coded values and back.
80 static const char coding
[90] =
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"
86 readletter(char letter
, unsigned char *ret
)
90 s
= strchr(coding
, letter
);
94 *ret
= (s
- coding
) % 16;
99 writeletter(unsigned char nibble
)
105 code
= (16 * (random() % 6)) + nibble
;
106 } while (code
>= 90);
110 ////////////////////////////////////////////////////////////
116 static struct savefile
*
117 savefile_open(const char *name
, bool forwrite
)
121 sf
= malloc(sizeof(*sf
));
125 sf
->f
= fopen(name
, forwrite
? "w" : "r");
129 "Hmm. The name \"%s\" appears to be magically blocked.\n",
138 memset(sf
->pad
, 0, sizeof(sf
->pad
));
147 savefile_rawread(struct savefile
*sf
, void *data
, size_t len
)
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
);
164 savefile_rawwrite(struct savefile
*sf
, const void *data
, size_t len
)
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
);
181 savefile_close(struct savefile
*sf
)
185 if (sf
->bintextpos
> 0) {
186 savefile_rawwrite(sf
, "\n", 1);
192 fprintf(stderr
, "Oops: error on %s.\n", sf
->name
);
201 * Read encoded binary data, discarding any whitespace that appears.
204 savefile_bintextread(struct savefile
*sf
, void *data
, size_t len
)
207 unsigned char *udata
;
214 if (ch
== EOF
|| ferror(sf
->f
)) {
215 fprintf(stderr
, "Oops: error reading %s.\n", sf
->name
);
219 if (ch
== ' ' || ch
== '\t' || ch
== '\r' || ch
== '\n') {
228 * Read binary data, decoding from text using readletter().
231 savefile_binread(struct savefile
*sf
, void *data
, size_t len
)
233 unsigned char buf
[64];
234 unsigned char *udata
;
235 unsigned char val1
, val2
;
242 if (amt
> sizeof(buf
) / 2) {
243 amt
= sizeof(buf
) / 2;
245 if (savefile_bintextread(sf
, buf
, amt
*2)) {
248 for (i
=0; i
<amt
; i
++) {
249 if (readletter(buf
[i
*2], &val1
)) {
252 if (readletter(buf
[i
*2 + 1], &val2
)) {
255 udata
[pos
++] = val1
* 16 + val2
;
262 * Write encoded binary data, inserting newlines to get a neatly
266 savefile_bintextwrite(struct savefile
*sf
, const void *data
, size_t len
)
269 const unsigned char *udata
;
274 amt
= BINTEXT_WIDTH
- sf
->bintextpos
;
275 if (amt
> len
- pos
) {
278 if (savefile_rawwrite(sf
, udata
+ pos
, amt
)) {
282 sf
->bintextpos
+= amt
;
283 if (sf
->bintextpos
>= BINTEXT_WIDTH
) {
284 savefile_rawwrite(sf
, "\n", 1);
292 * Write binary data, encoding as text using writeletter().
295 savefile_binwrite(struct savefile
*sf
, const void *data
, size_t len
)
297 unsigned char buf
[64];
298 const unsigned char *udata
;
307 buf
[bpos
++] = writeletter(byte
>> 4);
308 buf
[bpos
++] = writeletter(byte
& 0xf);
309 if (bpos
>= sizeof(buf
)) {
310 if (savefile_bintextwrite(sf
, buf
, bpos
)) {
316 if (savefile_bintextwrite(sf
, buf
, bpos
)) {
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.
330 * Make cheesy hash of buf[0..buflen]. Note: buf and outhash may overlap.
333 hash(const void *data
, size_t datalen
, unsigned char *out
, size_t outlen
)
335 const unsigned char *udata
;
338 const unsigned char *uval
;
343 for (i
=0; i
<datalen
; i
++) {
344 val
= val
^ 0xbadc0ffee;
345 val
= (val
<< 4) | (val
>> 60);
346 val
+= udata
[i
] ^ 0xbeefU
;
349 uval
= (unsigned char *)&val
;
351 for (i
=0; i
<outlen
; i
++) {
352 out
[i
] = uval
[valpos
++];
353 if (valpos
>= sizeof(val
)) {
360 * Set the "encryption" key.
363 savefile_key(struct savefile
*sf
, uint32_t key
)
367 hash(&sf
->key
, sizeof(sf
->key
), sf
->pad
, sizeof(sf
->pad
));
372 * Get an "encryption" pad byte. This forms a stream "cipher" that we
373 * xor with the plaintext save data.
376 savefile_getpad(struct savefile
*sf
)
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
));
389 * Read "encrypted" data.
392 savefile_cread(struct savefile
*sf
, void *data
, size_t len
)
395 unsigned char *udata
;
403 if (amt
> sizeof(buf
)) {
406 if (savefile_binread(sf
, buf
, amt
)) {
409 for (i
=0; i
<amt
; i
++) {
411 ch
^= savefile_getpad(sf
);
416 crc_add(&sf
->crc
, data
, len
);
421 * Write "encrypted" data.
424 savefile_cwrite(struct savefile
*sf
, const void *data
, size_t len
)
427 const unsigned char *udata
;
435 if (amt
> sizeof(buf
)) {
438 for (i
=0; i
<amt
; i
++) {
440 ch
^= savefile_getpad(sf
);
443 if (savefile_binwrite(sf
, buf
, amt
)) {
448 crc_add(&sf
->crc
, data
, len
);
452 ////////////////////////////////////////////////////////////
453 // compat for old save files
455 struct compat_saveinfo
{
460 static const struct compat_saveinfo compat_savearray
[] =
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
)},
484 {&knfloc
, sizeof(knfloc
)},
486 {&latency
, sizeof(latency
)},
487 {&limit
, sizeof(limit
)},
488 {&lmwarn
, sizeof(lmwarn
)},
490 {&maxdie
, sizeof(maxdie
)},
491 {&maxscore
, sizeof(maxscore
)},
492 {&newloc
, sizeof(newloc
)},
493 {&numdie
, sizeof(numdie
)},
495 {&oldloc2
, sizeof(oldloc2
)},
496 {&oldloc
, sizeof(oldloc
)},
497 {&panic
, sizeof(panic
)},
498 {&saveday
, sizeof(saveday
)},
499 {&savet
, sizeof(savet
)},
500 {&scoring
, sizeof(scoring
)},
502 {&stick
, sizeof(stick
)},
503 {&tally
, sizeof(tally
)},
504 {&tally2
, sizeof(tally2
)},
506 {&turns
, sizeof(turns
)},
507 {&verb
, sizeof(verb
)},
510 {&wasdark
, sizeof(wasdark
)},
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
)},
527 compat_restore(const char *infile
)
530 const struct compat_saveinfo
*p
;
536 if ((in
= fopen(infile
, "rb")) == NULL
) {
538 "Hmm. The file \"%s\" appears to be magically blocked.\n",
542 fread(&sum
, sizeof(sum
), 1, in
); /* Get the seed */
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 */
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 */
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.
571 ////////////////////////////////////////////////////////////
574 static int *const save_ints
[] = {
623 static const unsigned num_save_ints
= __arraycount(save_ints
);
625 #define INTARRAY(sym) { sym, __arraycount(sym) }
627 static const struct {
630 } save_intarrays
[] = {
642 static const unsigned num_save_intarrays
= __arraycount(save_intarrays
);
647 static const struct {
651 { &wd1
, sizeof(wd1
) },
652 { &wd2
, sizeof(wd2
) },
653 { &tkk
, sizeof(tkk
) },
655 static const unsigned num_save_blobs
= __arraycount(save_blobs
);
659 * Write out a save file. Returns nonzero on error.
662 save(const char *outfile
)
666 uint32_t key
, writeable_key
;
671 sf
= savefile_open(outfile
, true);
676 if (savefile_rawwrite(sf
, header
, strlen(header
))) {
681 version
= htonl(FORMAT_VERSION
);
682 if (savefile_binwrite(sf
, &version
, sizeof(version
))) {
687 clock_gettime(CLOCK_REALTIME
, &now
);
688 key
= (uint32_t)(now
.tv_sec
& 0xffffffff) ^ (uint32_t)(now
.tv_nsec
);
690 writeable_key
= htonl(key
);
691 if (savefile_binwrite(sf
, &writeable_key
, sizeof(writeable_key
))) {
696 /* other parts of the code may depend on us doing this here */
699 savefile_key(sf
, key
);
704 for (i
=0; i
<num_save_ints
; i
++) {
705 val
= *(save_ints
[i
]);
707 if (savefile_cwrite(sf
, &val
, sizeof(val
))) {
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
];
721 if (savefile_cwrite(sf
, &val
, sizeof(val
))) {
732 for (i
=0; i
<num_save_blobs
; i
++) {
733 if (savefile_cwrite(sf
, save_blobs
[i
].ptr
, save_blobs
[i
].len
)) {
740 sum
= htonl(crc_get(&sf
->crc
));
741 if (savefile_binwrite(sf
, &sum
, sizeof(&sum
))) {
750 * Read in a save file. Returns nonzero on error.
753 restore(const char *infile
)
756 char buf
[sizeof(header
)];
757 size_t headersize
= strlen(header
);
758 uint32_t version
, key
, sum
;
761 bool skipsum
= false;
763 sf
= savefile_open(infile
, false);
768 if (savefile_rawread(sf
, buf
, headersize
)) {
773 if (strcmp(buf
, header
) != 0) {
775 fprintf(stderr
, "Oh dear, that isn't one of my save files.\n");
777 "Trying the Olde Waye; this myte notte Worke.\n");
778 return compat_restore(infile
);
781 if (savefile_binread(sf
, &version
, sizeof(version
))) {
785 version
= ntohl(version
);
789 case FORMAT_VERSION_NOSUM
:
795 "Oh dear, that file must be from the future. I don't know"
796 " how to read it!\n");
800 if (savefile_binread(sf
, &key
, sizeof(key
))) {
805 savefile_key(sf
, key
);
807 /* other parts of the code may depend on us doing this here */
813 for (i
=0; i
<num_save_ints
; i
++) {
814 if (savefile_cread(sf
, &val
, sizeof(val
))) {
819 *(save_ints
[i
]) = val
;
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
))) {
833 save_intarrays
[i
].ptr
[j
] = val
;
841 for (i
=0; i
<num_save_blobs
; i
++) {
842 if (savefile_cread(sf
, save_blobs
[i
].ptr
, save_blobs
[i
].len
)) {
849 if (savefile_binread(sf
, &sum
, sizeof(&sum
))) {
854 /* See if she cheated */
855 if (!skipsum
&& sum
!= crc_get(&sf
->crc
)) {
856 /* Tsk tsk, altered the file */
862 /* Load theoretically invalidates these */