save.c revision 1.14 1 1.14 dholland /* $NetBSD: save.c,v 1.14 2014/03/22 22:04:40 dholland Exp $ */
2 1.2 cgd
3 1.1 jtc /*-
4 1.1 jtc * Copyright (c) 1991, 1993
5 1.1 jtc * The Regents of the University of California. All rights reserved.
6 1.1 jtc *
7 1.1 jtc * The game adventure was originally written in Fortran by Will Crowther
8 1.1 jtc * and Don Woods. It was later translated to C and enhanced by Jim
9 1.1 jtc * Gillogly. This code is derived from software contributed to Berkeley
10 1.1 jtc * by Jim Gillogly at The Rand Corporation.
11 1.1 jtc *
12 1.1 jtc * Redistribution and use in source and binary forms, with or without
13 1.1 jtc * modification, are permitted provided that the following conditions
14 1.1 jtc * are met:
15 1.1 jtc * 1. Redistributions of source code must retain the above copyright
16 1.1 jtc * notice, this list of conditions and the following disclaimer.
17 1.1 jtc * 2. Redistributions in binary form must reproduce the above copyright
18 1.1 jtc * notice, this list of conditions and the following disclaimer in the
19 1.1 jtc * documentation and/or other materials provided with the distribution.
20 1.8 agc * 3. Neither the name of the University nor the names of its contributors
21 1.1 jtc * may be used to endorse or promote products derived from this software
22 1.1 jtc * without specific prior written permission.
23 1.1 jtc *
24 1.1 jtc * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25 1.1 jtc * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 1.1 jtc * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 1.1 jtc * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28 1.1 jtc * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 1.1 jtc * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 1.1 jtc * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 1.1 jtc * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 1.1 jtc * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 1.1 jtc * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34 1.1 jtc * SUCH DAMAGE.
35 1.1 jtc */
36 1.1 jtc
37 1.3 christos #include <sys/cdefs.h>
38 1.1 jtc #ifndef lint
39 1.2 cgd #if 0
40 1.1 jtc static char sccsid[] = "@(#)save.c 8.1 (Berkeley) 5/31/93";
41 1.2 cgd #else
42 1.14 dholland __RCSID("$NetBSD: save.c,v 1.14 2014/03/22 22:04:40 dholland Exp $");
43 1.2 cgd #endif
44 1.4 lukem #endif /* not lint */
45 1.1 jtc
46 1.12 dholland #include <sys/types.h>
47 1.12 dholland #include <sys/time.h>
48 1.12 dholland #include <stdbool.h>
49 1.1 jtc #include <stdio.h>
50 1.3 christos #include <stdlib.h>
51 1.12 dholland #include <err.h>
52 1.12 dholland #include <assert.h>
53 1.12 dholland
54 1.1 jtc #include "hdr.h"
55 1.3 christos #include "extern.h"
56 1.1 jtc
57 1.12 dholland struct savefile {
58 1.12 dholland FILE *f;
59 1.12 dholland const char *name;
60 1.12 dholland bool warned;
61 1.14 dholland size_t bintextpos;
62 1.12 dholland uint32_t key;
63 1.13 dholland struct crcstate crc;
64 1.12 dholland unsigned char pad[8];
65 1.12 dholland unsigned padpos;
66 1.12 dholland };
67 1.12 dholland
68 1.12 dholland #define BINTEXT_WIDTH 60
69 1.13 dholland #define FORMAT_VERSION 2
70 1.13 dholland #define FORMAT_VERSION_NOSUM 1
71 1.12 dholland static const char header[] = "Adventure save file\n";
72 1.12 dholland
73 1.12 dholland ////////////////////////////////////////////////////////////
74 1.12 dholland // base16 output encoding
75 1.12 dholland
76 1.12 dholland /*
77 1.12 dholland * Map 16 plain values into 90 coded values and back.
78 1.12 dholland */
79 1.12 dholland
80 1.12 dholland static const char coding[90] =
81 1.12 dholland "Db.GOyT]7a6zpF(c*5H9oK~0[WVAg&kR)ml,2^q-1Y3v+"
82 1.12 dholland "X/=JirZL$C>_N?:}B{dfnsxU<@MQ%8|P!4h`ESt;euwIj"
83 1.12 dholland ;
84 1.12 dholland
85 1.12 dholland static int
86 1.12 dholland readletter(char letter, unsigned char *ret)
87 1.12 dholland {
88 1.12 dholland const char *s;
89 1.12 dholland
90 1.12 dholland s = strchr(coding, letter);
91 1.12 dholland if (s == NULL) {
92 1.12 dholland return 1;
93 1.12 dholland }
94 1.12 dholland *ret = (s - coding) % 16;
95 1.12 dholland return 0;
96 1.12 dholland }
97 1.12 dholland
98 1.12 dholland static char
99 1.12 dholland writeletter(unsigned char nibble)
100 1.12 dholland {
101 1.12 dholland unsigned code;
102 1.12 dholland
103 1.12 dholland assert(nibble < 16);
104 1.12 dholland do {
105 1.12 dholland code = (16 * (random() % 6)) + nibble;
106 1.12 dholland } while (code >= 90);
107 1.12 dholland return coding[code];
108 1.12 dholland }
109 1.12 dholland
110 1.12 dholland ////////////////////////////////////////////////////////////
111 1.12 dholland // savefile
112 1.12 dholland
113 1.12 dholland /*
114 1.12 dholland * Open a savefile.
115 1.12 dholland */
116 1.12 dholland static struct savefile *
117 1.12 dholland savefile_open(const char *name, bool forwrite)
118 1.12 dholland {
119 1.12 dholland struct savefile *sf;
120 1.12 dholland
121 1.12 dholland sf = malloc(sizeof(*sf));
122 1.12 dholland if (sf == NULL) {
123 1.12 dholland return NULL;
124 1.12 dholland }
125 1.12 dholland sf->f = fopen(name, forwrite ? "w" : "r");
126 1.12 dholland if (sf->f == NULL) {
127 1.12 dholland free(sf);
128 1.12 dholland fprintf(stderr,
129 1.12 dholland "Hmm. The name \"%s\" appears to be magically blocked.\n",
130 1.12 dholland name);
131 1.12 dholland return NULL;
132 1.12 dholland }
133 1.12 dholland sf->name = name;
134 1.12 dholland sf->warned = false;
135 1.12 dholland sf->bintextpos = 0;
136 1.12 dholland sf->key = 0;
137 1.13 dholland crc_start(&sf->crc);
138 1.12 dholland memset(sf->pad, 0, sizeof(sf->pad));
139 1.12 dholland sf->padpos = 0;
140 1.12 dholland return sf;
141 1.12 dholland }
142 1.12 dholland
143 1.12 dholland /*
144 1.12 dholland * Raw read.
145 1.12 dholland */
146 1.12 dholland static int
147 1.12 dholland savefile_rawread(struct savefile *sf, void *data, size_t len)
148 1.12 dholland {
149 1.12 dholland size_t result;
150 1.12 dholland
151 1.12 dholland result = fread(data, 1, len, sf->f);
152 1.12 dholland if (result != len || ferror(sf->f)) {
153 1.12 dholland fprintf(stderr, "Oops: error reading %s.\n", sf->name);
154 1.12 dholland sf->warned = true;
155 1.12 dholland return 1;
156 1.12 dholland }
157 1.12 dholland return 0;
158 1.12 dholland }
159 1.12 dholland
160 1.12 dholland /*
161 1.12 dholland * Raw write.
162 1.12 dholland */
163 1.12 dholland static int
164 1.12 dholland savefile_rawwrite(struct savefile *sf, const void *data, size_t len)
165 1.12 dholland {
166 1.12 dholland size_t result;
167 1.12 dholland
168 1.12 dholland result = fwrite(data, 1, len, sf->f);
169 1.12 dholland if (result != len || ferror(sf->f)) {
170 1.12 dholland fprintf(stderr, "Oops: error writing %s.\n", sf->name);
171 1.12 dholland sf->warned = true;
172 1.12 dholland return 1;
173 1.12 dholland }
174 1.12 dholland return 0;
175 1.12 dholland }
176 1.12 dholland
177 1.12 dholland /*
178 1.12 dholland * Close a savefile.
179 1.12 dholland */
180 1.12 dholland static int
181 1.12 dholland savefile_close(struct savefile *sf)
182 1.12 dholland {
183 1.12 dholland int ret;
184 1.12 dholland
185 1.12 dholland if (sf->bintextpos > 0) {
186 1.12 dholland savefile_rawwrite(sf, "\n", 1);
187 1.12 dholland }
188 1.12 dholland
189 1.12 dholland ret = 0;
190 1.12 dholland if (fclose(sf->f)) {
191 1.12 dholland if (!sf->warned) {
192 1.12 dholland fprintf(stderr, "Oops: error on %s.\n", sf->name);
193 1.12 dholland }
194 1.12 dholland ret = 1;
195 1.12 dholland }
196 1.12 dholland free(sf);
197 1.12 dholland return ret;
198 1.12 dholland }
199 1.12 dholland
200 1.12 dholland /*
201 1.12 dholland * Read encoded binary data, discarding any whitespace that appears.
202 1.12 dholland */
203 1.12 dholland static int
204 1.12 dholland savefile_bintextread(struct savefile *sf, void *data, size_t len)
205 1.12 dholland {
206 1.12 dholland size_t pos;
207 1.12 dholland unsigned char *udata;
208 1.12 dholland int ch;
209 1.12 dholland
210 1.12 dholland udata = data;
211 1.12 dholland pos = 0;
212 1.12 dholland while (pos < len) {
213 1.12 dholland ch = fgetc(sf->f);
214 1.12 dholland if (ch == EOF || ferror(sf->f)) {
215 1.12 dholland fprintf(stderr, "Oops: error reading %s.\n", sf->name);
216 1.12 dholland sf->warned = true;
217 1.12 dholland return 1;
218 1.12 dholland }
219 1.12 dholland if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') {
220 1.12 dholland continue;
221 1.12 dholland }
222 1.12 dholland udata[pos++] = ch;
223 1.12 dholland }
224 1.12 dholland return 0;
225 1.12 dholland }
226 1.12 dholland
227 1.12 dholland /*
228 1.12 dholland * Read binary data, decoding from text using readletter().
229 1.12 dholland */
230 1.12 dholland static int
231 1.12 dholland savefile_binread(struct savefile *sf, void *data, size_t len)
232 1.12 dholland {
233 1.12 dholland unsigned char buf[64];
234 1.12 dholland unsigned char *udata;
235 1.12 dholland unsigned char val1, val2;
236 1.12 dholland size_t pos, amt, i;
237 1.12 dholland
238 1.12 dholland udata = data;
239 1.12 dholland pos = 0;
240 1.12 dholland while (pos < len) {
241 1.12 dholland amt = len - pos;
242 1.12 dholland if (amt > sizeof(buf) / 2) {
243 1.12 dholland amt = sizeof(buf) / 2;
244 1.12 dholland }
245 1.12 dholland if (savefile_bintextread(sf, buf, amt*2)) {
246 1.12 dholland return 1;
247 1.12 dholland }
248 1.12 dholland for (i=0; i<amt; i++) {
249 1.12 dholland if (readletter(buf[i*2], &val1)) {
250 1.12 dholland return 1;
251 1.12 dholland }
252 1.12 dholland if (readletter(buf[i*2 + 1], &val2)) {
253 1.12 dholland return 1;
254 1.12 dholland }
255 1.12 dholland udata[pos++] = val1 * 16 + val2;
256 1.12 dholland }
257 1.12 dholland }
258 1.12 dholland return 0;
259 1.12 dholland }
260 1.12 dholland
261 1.12 dholland /*
262 1.12 dholland * Write encoded binary data, inserting newlines to get a neatly
263 1.12 dholland * formatted block.
264 1.12 dholland */
265 1.12 dholland static int
266 1.12 dholland savefile_bintextwrite(struct savefile *sf, const void *data, size_t len)
267 1.12 dholland {
268 1.12 dholland size_t pos, amt;
269 1.12 dholland const unsigned char *udata;
270 1.12 dholland
271 1.12 dholland udata = data;
272 1.12 dholland pos = 0;
273 1.12 dholland while (pos < len) {
274 1.12 dholland amt = BINTEXT_WIDTH - sf->bintextpos;
275 1.12 dholland if (amt > len - pos) {
276 1.12 dholland amt = len - pos;
277 1.12 dholland }
278 1.12 dholland if (savefile_rawwrite(sf, udata + pos, amt)) {
279 1.12 dholland return 1;
280 1.12 dholland }
281 1.12 dholland pos += amt;
282 1.12 dholland sf->bintextpos += amt;
283 1.12 dholland if (sf->bintextpos >= BINTEXT_WIDTH) {
284 1.12 dholland savefile_rawwrite(sf, "\n", 1);
285 1.12 dholland sf->bintextpos = 0;
286 1.12 dholland }
287 1.12 dholland }
288 1.12 dholland return 0;
289 1.12 dholland }
290 1.12 dholland
291 1.12 dholland /*
292 1.12 dholland * Write binary data, encoding as text using writeletter().
293 1.12 dholland */
294 1.12 dholland static int
295 1.12 dholland savefile_binwrite(struct savefile *sf, const void *data, size_t len)
296 1.12 dholland {
297 1.12 dholland unsigned char buf[64];
298 1.12 dholland const unsigned char *udata;
299 1.12 dholland size_t pos, bpos;
300 1.12 dholland unsigned char byte;
301 1.12 dholland
302 1.12 dholland udata = data;
303 1.12 dholland pos = 0;
304 1.12 dholland bpos = 0;
305 1.12 dholland while (pos < len) {
306 1.12 dholland byte = udata[pos++];
307 1.12 dholland buf[bpos++] = writeletter(byte >> 4);
308 1.12 dholland buf[bpos++] = writeletter(byte & 0xf);
309 1.12 dholland if (bpos >= sizeof(buf)) {
310 1.12 dholland if (savefile_bintextwrite(sf, buf, bpos)) {
311 1.12 dholland return 1;
312 1.12 dholland }
313 1.12 dholland bpos = 0;
314 1.12 dholland }
315 1.12 dholland }
316 1.12 dholland if (savefile_bintextwrite(sf, buf, bpos)) {
317 1.12 dholland return 1;
318 1.12 dholland }
319 1.12 dholland return 0;
320 1.12 dholland }
321 1.12 dholland
322 1.12 dholland /*
323 1.12 dholland * Lightweight "encryption" for save files. This is not meant to
324 1.12 dholland * be secure and wouldn't be even if we didn't write the decrypt
325 1.12 dholland * key to the beginning of the save file; it's just meant to be
326 1.12 dholland * enough to discourage casual cheating.
327 1.12 dholland */
328 1.12 dholland
329 1.12 dholland /*
330 1.12 dholland * Make cheesy hash of buf[0..buflen]. Note: buf and outhash may overlap.
331 1.12 dholland */
332 1.12 dholland static void
333 1.12 dholland hash(const void *data, size_t datalen, unsigned char *out, size_t outlen)
334 1.12 dholland {
335 1.12 dholland const unsigned char *udata;
336 1.12 dholland size_t i;
337 1.12 dholland uint64_t val;
338 1.12 dholland const unsigned char *uval;
339 1.12 dholland size_t valpos;
340 1.12 dholland
341 1.12 dholland udata = data;
342 1.12 dholland val = 0;
343 1.12 dholland for (i=0; i<datalen; i++) {
344 1.12 dholland val = val ^ 0xbadc0ffee;
345 1.12 dholland val = (val << 4) | (val >> 60);
346 1.14 dholland val += udata[i] ^ 0xbeefU;
347 1.12 dholland }
348 1.12 dholland
349 1.12 dholland uval = (unsigned char *)&val;
350 1.12 dholland valpos = 0;
351 1.12 dholland for (i=0; i<outlen; i++) {
352 1.12 dholland out[i] = uval[valpos++];
353 1.12 dholland if (valpos >= sizeof(val)) {
354 1.12 dholland valpos = 0;
355 1.12 dholland }
356 1.12 dholland }
357 1.12 dholland }
358 1.12 dholland
359 1.12 dholland /*
360 1.12 dholland * Set the "encryption" key.
361 1.12 dholland */
362 1.12 dholland static void
363 1.12 dholland savefile_key(struct savefile *sf, uint32_t key)
364 1.12 dholland {
365 1.12 dholland sf->key = 0;
366 1.13 dholland crc_start(&sf->crc);
367 1.12 dholland hash(&sf->key, sizeof(sf->key), sf->pad, sizeof(sf->pad));
368 1.12 dholland sf->padpos = 0;
369 1.12 dholland }
370 1.12 dholland
371 1.12 dholland /*
372 1.12 dholland * Get an "encryption" pad byte. This forms a stream "cipher" that we
373 1.12 dholland * xor with the plaintext save data.
374 1.12 dholland */
375 1.12 dholland static unsigned char
376 1.12 dholland savefile_getpad(struct savefile *sf)
377 1.12 dholland {
378 1.12 dholland unsigned char ret;
379 1.12 dholland
380 1.12 dholland ret = sf->pad[sf->padpos++];
381 1.12 dholland if (sf->padpos >= sizeof(sf->pad)) {
382 1.12 dholland hash(sf->pad, sizeof(sf->pad), sf->pad, sizeof(sf->pad));
383 1.12 dholland sf->padpos = 0;
384 1.12 dholland }
385 1.12 dholland return ret;
386 1.12 dholland }
387 1.12 dholland
388 1.12 dholland /*
389 1.12 dholland * Read "encrypted" data.
390 1.12 dholland */
391 1.12 dholland static int
392 1.12 dholland savefile_cread(struct savefile *sf, void *data, size_t len)
393 1.12 dholland {
394 1.12 dholland char buf[64];
395 1.12 dholland unsigned char *udata;
396 1.12 dholland size_t pos, amt, i;
397 1.12 dholland unsigned char ch;
398 1.12 dholland
399 1.12 dholland udata = data;
400 1.12 dholland pos = 0;
401 1.12 dholland while (pos < len) {
402 1.12 dholland amt = len - pos;
403 1.12 dholland if (amt > sizeof(buf)) {
404 1.12 dholland amt = sizeof(buf);
405 1.12 dholland }
406 1.12 dholland if (savefile_binread(sf, buf, amt)) {
407 1.12 dholland return 1;
408 1.12 dholland }
409 1.12 dholland for (i=0; i<amt; i++) {
410 1.12 dholland ch = buf[i];
411 1.12 dholland ch ^= savefile_getpad(sf);
412 1.12 dholland udata[pos + i] = ch;
413 1.12 dholland }
414 1.12 dholland pos += amt;
415 1.12 dholland }
416 1.13 dholland crc_add(&sf->crc, data, len);
417 1.12 dholland return 0;
418 1.12 dholland }
419 1.12 dholland
420 1.12 dholland /*
421 1.12 dholland * Write "encrypted" data.
422 1.12 dholland */
423 1.12 dholland static int
424 1.12 dholland savefile_cwrite(struct savefile *sf, const void *data, size_t len)
425 1.12 dholland {
426 1.12 dholland char buf[64];
427 1.12 dholland const unsigned char *udata;
428 1.12 dholland size_t pos, amt, i;
429 1.12 dholland unsigned char ch;
430 1.12 dholland
431 1.12 dholland udata = data;
432 1.12 dholland pos = 0;
433 1.12 dholland while (pos < len) {
434 1.12 dholland amt = len - pos;
435 1.12 dholland if (amt > sizeof(buf)) {
436 1.12 dholland amt = sizeof(buf);
437 1.12 dholland }
438 1.12 dholland for (i=0; i<amt; i++) {
439 1.12 dholland ch = udata[pos + i];
440 1.12 dholland ch ^= savefile_getpad(sf);
441 1.12 dholland buf[i] = ch;
442 1.12 dholland }
443 1.12 dholland if (savefile_binwrite(sf, buf, amt)) {
444 1.12 dholland return 1;
445 1.12 dholland }
446 1.12 dholland pos += amt;
447 1.12 dholland }
448 1.13 dholland crc_add(&sf->crc, data, len);
449 1.12 dholland return 0;
450 1.12 dholland }
451 1.12 dholland
452 1.12 dholland ////////////////////////////////////////////////////////////
453 1.12 dholland // compat for old save files
454 1.12 dholland
455 1.12 dholland struct compat_saveinfo {
456 1.4 lukem void *address;
457 1.14 dholland size_t width;
458 1.1 jtc };
459 1.1 jtc
460 1.12 dholland static const struct compat_saveinfo compat_savearray[] =
461 1.1 jtc {
462 1.4 lukem {&abbnum, sizeof(abbnum)},
463 1.4 lukem {&attack, sizeof(attack)},
464 1.4 lukem {&blklin, sizeof(blklin)},
465 1.4 lukem {&bonus, sizeof(bonus)},
466 1.4 lukem {&chloc, sizeof(chloc)},
467 1.4 lukem {&chloc2, sizeof(chloc2)},
468 1.4 lukem {&clock1, sizeof(clock1)},
469 1.4 lukem {&clock2, sizeof(clock2)},
470 1.4 lukem {&closed, sizeof(closed)},
471 1.11 dholland {&isclosing, sizeof(isclosing)},
472 1.11 dholland {&daltloc, sizeof(daltloc)},
473 1.4 lukem {&demo, sizeof(demo)},
474 1.4 lukem {&detail, sizeof(detail)},
475 1.4 lukem {&dflag, sizeof(dflag)},
476 1.4 lukem {&dkill, sizeof(dkill)},
477 1.4 lukem {&dtotal, sizeof(dtotal)},
478 1.4 lukem {&foobar, sizeof(foobar)},
479 1.4 lukem {&gaveup, sizeof(gaveup)},
480 1.11 dholland {&holding, sizeof(holding)},
481 1.4 lukem {&iwest, sizeof(iwest)},
482 1.4 lukem {&k, sizeof(k)},
483 1.4 lukem {&k2, sizeof(k2)},
484 1.4 lukem {&knfloc, sizeof(knfloc)},
485 1.4 lukem {&kq, sizeof(kq)},
486 1.11 dholland {&latency, sizeof(latency)},
487 1.4 lukem {&limit, sizeof(limit)},
488 1.4 lukem {&lmwarn, sizeof(lmwarn)},
489 1.4 lukem {&loc, sizeof(loc)},
490 1.4 lukem {&maxdie, sizeof(maxdie)},
491 1.11 dholland {&maxscore, sizeof(maxscore)},
492 1.4 lukem {&newloc, sizeof(newloc)},
493 1.4 lukem {&numdie, sizeof(numdie)},
494 1.4 lukem {&obj, sizeof(obj)},
495 1.11 dholland {&oldloc2, sizeof(oldloc2)},
496 1.4 lukem {&oldloc, sizeof(oldloc)},
497 1.4 lukem {&panic, sizeof(panic)},
498 1.6 hubertf {&saveday, sizeof(saveday)},
499 1.4 lukem {&savet, sizeof(savet)},
500 1.11 dholland {&scoring, sizeof(scoring)},
501 1.4 lukem {&spk, sizeof(spk)},
502 1.4 lukem {&stick, sizeof(stick)},
503 1.4 lukem {&tally, sizeof(tally)},
504 1.4 lukem {&tally2, sizeof(tally2)},
505 1.4 lukem {&tkk, sizeof(tkk)},
506 1.4 lukem {&turns, sizeof(turns)},
507 1.4 lukem {&verb, sizeof(verb)},
508 1.4 lukem {&wd1, sizeof(wd1)},
509 1.4 lukem {&wd2, sizeof(wd2)},
510 1.11 dholland {&wasdark, sizeof(wasdark)},
511 1.4 lukem {&yea, sizeof(yea)},
512 1.4 lukem {atloc, sizeof(atloc)},
513 1.4 lukem {dloc, sizeof(dloc)},
514 1.4 lukem {dseen, sizeof(dseen)},
515 1.4 lukem {fixed, sizeof(fixed)},
516 1.4 lukem {hinted, sizeof(hinted)},
517 1.4 lukem {links, sizeof(links)},
518 1.4 lukem {odloc, sizeof(odloc)},
519 1.4 lukem {place, sizeof(place)},
520 1.4 lukem {prop, sizeof(prop)},
521 1.4 lukem {tk, sizeof(tk)},
522 1.1 jtc
523 1.4 lukem {NULL, 0}
524 1.1 jtc };
525 1.1 jtc
526 1.12 dholland static int
527 1.12 dholland compat_restore(const char *infile)
528 1.1 jtc {
529 1.4 lukem FILE *in;
530 1.12 dholland const struct compat_saveinfo *p;
531 1.4 lukem char *s;
532 1.4 lukem long sum, cksum = 0;
533 1.14 dholland size_t i;
534 1.13 dholland struct crcstate crc;
535 1.4 lukem
536 1.4 lukem if ((in = fopen(infile, "rb")) == NULL) {
537 1.4 lukem fprintf(stderr,
538 1.4 lukem "Hmm. The file \"%s\" appears to be magically blocked.\n",
539 1.4 lukem infile);
540 1.4 lukem return 1;
541 1.1 jtc }
542 1.4 lukem fread(&sum, sizeof(sum), 1, in); /* Get the seed */
543 1.1 jtc srandom((int) sum);
544 1.12 dholland for (p = compat_savearray; p->address != NULL; p++) {
545 1.1 jtc fread(p->address, p->width, 1, in);
546 1.1 jtc for (s = p->address, i = 0; i < p->width; i++, s++)
547 1.4 lukem *s = (*s ^ random()) & 0xFF; /* Lightly decrypt */
548 1.1 jtc }
549 1.1 jtc fclose(in);
550 1.1 jtc
551 1.13 dholland crc_start(&crc); /* See if she cheated */
552 1.12 dholland for (p = compat_savearray; p->address != NULL; p++)
553 1.13 dholland crc_add(&crc, p->address, p->width);
554 1.13 dholland cksum = crc_get(&crc);
555 1.4 lukem if (sum != cksum) /* Tsk tsk */
556 1.4 lukem return 2; /* Altered the file */
557 1.1 jtc /* We successfully restored, so this really was a save file */
558 1.12 dholland
559 1.12 dholland /*
560 1.12 dholland * The above code loads these from disk even though they're
561 1.12 dholland * pointers. Null them out and hope we don't crash on them
562 1.12 dholland * later; that's better than having them be garbage.
563 1.12 dholland */
564 1.12 dholland tkk = NULL;
565 1.12 dholland wd1 = NULL;
566 1.12 dholland wd2 = NULL;
567 1.12 dholland
568 1.12 dholland return 0;
569 1.12 dholland }
570 1.12 dholland
571 1.12 dholland ////////////////////////////////////////////////////////////
572 1.12 dholland // save + restore
573 1.12 dholland
574 1.12 dholland static int *const save_ints[] = {
575 1.12 dholland &abbnum,
576 1.12 dholland &attack,
577 1.12 dholland &blklin,
578 1.12 dholland &bonus,
579 1.12 dholland &chloc,
580 1.12 dholland &chloc2,
581 1.12 dholland &clock1,
582 1.12 dholland &clock2,
583 1.12 dholland &closed,
584 1.12 dholland &isclosing,
585 1.12 dholland &daltloc,
586 1.12 dholland &demo,
587 1.12 dholland &detail,
588 1.12 dholland &dflag,
589 1.12 dholland &dkill,
590 1.12 dholland &dtotal,
591 1.12 dholland &foobar,
592 1.12 dholland &gaveup,
593 1.12 dholland &holding,
594 1.12 dholland &iwest,
595 1.12 dholland &k,
596 1.12 dholland &k2,
597 1.12 dholland &knfloc,
598 1.12 dholland &kq,
599 1.12 dholland &latency,
600 1.12 dholland &limit,
601 1.12 dholland &lmwarn,
602 1.12 dholland &loc,
603 1.12 dholland &maxdie,
604 1.12 dholland &maxscore,
605 1.12 dholland &newloc,
606 1.12 dholland &numdie,
607 1.12 dholland &obj,
608 1.12 dholland &oldloc2,
609 1.12 dholland &oldloc,
610 1.12 dholland &panic,
611 1.12 dholland &saveday,
612 1.12 dholland &savet,
613 1.12 dholland &scoring,
614 1.12 dholland &spk,
615 1.12 dholland &stick,
616 1.12 dholland &tally,
617 1.12 dholland &tally2,
618 1.12 dholland &turns,
619 1.12 dholland &verb,
620 1.12 dholland &wasdark,
621 1.12 dholland &yea,
622 1.12 dholland };
623 1.12 dholland static const unsigned num_save_ints = __arraycount(save_ints);
624 1.12 dholland
625 1.12 dholland #define INTARRAY(sym) { sym, __arraycount(sym) }
626 1.12 dholland
627 1.12 dholland static const struct {
628 1.12 dholland int *ptr;
629 1.12 dholland unsigned num;
630 1.12 dholland } save_intarrays[] = {
631 1.12 dholland INTARRAY(atloc),
632 1.12 dholland INTARRAY(dseen),
633 1.12 dholland INTARRAY(dloc),
634 1.12 dholland INTARRAY(odloc),
635 1.12 dholland INTARRAY(fixed),
636 1.12 dholland INTARRAY(hinted),
637 1.12 dholland INTARRAY(links),
638 1.12 dholland INTARRAY(place),
639 1.12 dholland INTARRAY(prop),
640 1.12 dholland INTARRAY(tk),
641 1.12 dholland };
642 1.12 dholland static const unsigned num_save_intarrays = __arraycount(save_intarrays);
643 1.12 dholland
644 1.12 dholland #undef INTARRAY
645 1.12 dholland
646 1.12 dholland #if 0
647 1.12 dholland static const struct {
648 1.12 dholland void *ptr;
649 1.12 dholland size_t len;
650 1.12 dholland } save_blobs[] = {
651 1.12 dholland { &wd1, sizeof(wd1) },
652 1.12 dholland { &wd2, sizeof(wd2) },
653 1.12 dholland { &tkk, sizeof(tkk) },
654 1.12 dholland };
655 1.12 dholland static const unsigned num_save_blobs = __arraycount(save_blobs);
656 1.12 dholland #endif
657 1.12 dholland
658 1.12 dholland /*
659 1.12 dholland * Write out a save file. Returns nonzero on error.
660 1.12 dholland */
661 1.12 dholland int
662 1.12 dholland save(const char *outfile)
663 1.12 dholland {
664 1.12 dholland struct savefile *sf;
665 1.12 dholland struct timespec now;
666 1.12 dholland uint32_t key, writeable_key;
667 1.12 dholland uint32_t version;
668 1.12 dholland unsigned i, j, n;
669 1.13 dholland uint32_t val, sum;
670 1.12 dholland
671 1.12 dholland sf = savefile_open(outfile, true);
672 1.12 dholland if (sf == NULL) {
673 1.12 dholland return 1;
674 1.12 dholland }
675 1.12 dholland
676 1.12 dholland if (savefile_rawwrite(sf, header, strlen(header))) {
677 1.12 dholland savefile_close(sf);
678 1.12 dholland return 1;
679 1.12 dholland }
680 1.12 dholland
681 1.12 dholland version = htonl(FORMAT_VERSION);
682 1.12 dholland if (savefile_binwrite(sf, &version, sizeof(version))) {
683 1.12 dholland savefile_close(sf);
684 1.12 dholland return 1;
685 1.12 dholland }
686 1.12 dholland
687 1.12 dholland clock_gettime(CLOCK_REALTIME, &now);
688 1.12 dholland key = (uint32_t)(now.tv_sec & 0xffffffff) ^ (uint32_t)(now.tv_nsec);
689 1.12 dholland
690 1.12 dholland writeable_key = htonl(key);
691 1.12 dholland if (savefile_binwrite(sf, &writeable_key, sizeof(writeable_key))) {
692 1.12 dholland savefile_close(sf);
693 1.12 dholland return 1;
694 1.12 dholland }
695 1.12 dholland
696 1.12 dholland /* other parts of the code may depend on us doing this here */
697 1.12 dholland srandom(key);
698 1.12 dholland
699 1.12 dholland savefile_key(sf, key);
700 1.12 dholland
701 1.12 dholland /*
702 1.12 dholland * Integers
703 1.12 dholland */
704 1.12 dholland for (i=0; i<num_save_ints; i++) {
705 1.12 dholland val = *(save_ints[i]);
706 1.12 dholland val = htonl(val);
707 1.12 dholland if (savefile_cwrite(sf, &val, sizeof(val))) {
708 1.12 dholland savefile_close(sf);
709 1.12 dholland return 1;
710 1.12 dholland }
711 1.12 dholland }
712 1.12 dholland
713 1.12 dholland /*
714 1.12 dholland * Arrays of integers
715 1.12 dholland */
716 1.12 dholland for (i=0; i<num_save_intarrays; i++) {
717 1.12 dholland n = save_intarrays[i].num;
718 1.12 dholland for (j=0; j<n; j++) {
719 1.12 dholland val = save_intarrays[i].ptr[j];
720 1.12 dholland val = htonl(val);
721 1.12 dholland if (savefile_cwrite(sf, &val, sizeof(val))) {
722 1.12 dholland savefile_close(sf);
723 1.12 dholland return 1;
724 1.12 dholland }
725 1.12 dholland }
726 1.12 dholland }
727 1.12 dholland
728 1.12 dholland #if 0
729 1.12 dholland /*
730 1.12 dholland * Blobs
731 1.12 dholland */
732 1.12 dholland for (i=0; i<num_save_blobs; i++) {
733 1.12 dholland if (savefile_cwrite(sf, save_blobs[i].ptr, save_blobs[i].len)) {
734 1.12 dholland savefile_close(sf);
735 1.12 dholland return 1;
736 1.12 dholland }
737 1.12 dholland }
738 1.12 dholland #endif
739 1.12 dholland
740 1.13 dholland sum = htonl(crc_get(&sf->crc));
741 1.13 dholland if (savefile_binwrite(sf, &sum, sizeof(&sum))) {
742 1.12 dholland savefile_close(sf);
743 1.12 dholland return 1;
744 1.12 dholland }
745 1.12 dholland savefile_close(sf);
746 1.12 dholland return 0;
747 1.12 dholland }
748 1.12 dholland
749 1.12 dholland /*
750 1.12 dholland * Read in a save file. Returns nonzero on error.
751 1.12 dholland */
752 1.12 dholland int
753 1.12 dholland restore(const char *infile)
754 1.12 dholland {
755 1.12 dholland struct savefile *sf;
756 1.12 dholland char buf[sizeof(header)];
757 1.12 dholland size_t headersize = strlen(header);
758 1.12 dholland uint32_t version, key, sum;
759 1.12 dholland unsigned i, j, n;
760 1.12 dholland uint32_t val;
761 1.13 dholland bool skipsum = false;
762 1.12 dholland
763 1.12 dholland sf = savefile_open(infile, false);
764 1.12 dholland if (sf == NULL) {
765 1.12 dholland return 1;
766 1.12 dholland }
767 1.12 dholland
768 1.12 dholland if (savefile_rawread(sf, buf, headersize)) {
769 1.12 dholland savefile_close(sf);
770 1.12 dholland return 1;
771 1.12 dholland }
772 1.12 dholland buf[headersize] = 0;
773 1.12 dholland if (strcmp(buf, header) != 0) {
774 1.12 dholland savefile_close(sf);
775 1.12 dholland fprintf(stderr, "Oh dear, that isn't one of my save files.\n");
776 1.12 dholland fprintf(stderr,
777 1.12 dholland "Trying the Olde Waye; this myte notte Worke.\n");
778 1.12 dholland return compat_restore(infile);
779 1.12 dholland }
780 1.12 dholland
781 1.12 dholland if (savefile_binread(sf, &version, sizeof(version))) {
782 1.12 dholland savefile_close(sf);
783 1.12 dholland return 1;
784 1.12 dholland }
785 1.12 dholland version = ntohl(version);
786 1.13 dholland switch (version) {
787 1.13 dholland case FORMAT_VERSION:
788 1.13 dholland break;
789 1.13 dholland case FORMAT_VERSION_NOSUM:
790 1.13 dholland skipsum = true;
791 1.13 dholland break;
792 1.13 dholland default:
793 1.12 dholland savefile_close(sf);
794 1.12 dholland fprintf(stderr,
795 1.12 dholland "Oh dear, that file must be from the future. I don't know"
796 1.12 dholland " how to read it!\n");
797 1.12 dholland return 1;
798 1.12 dholland }
799 1.12 dholland
800 1.12 dholland if (savefile_binread(sf, &key, sizeof(key))) {
801 1.12 dholland savefile_close(sf);
802 1.12 dholland return 1;
803 1.12 dholland }
804 1.12 dholland key = ntohl(key);
805 1.12 dholland savefile_key(sf, key);
806 1.12 dholland
807 1.12 dholland /* other parts of the code may depend on us doing this here */
808 1.12 dholland srandom(key);
809 1.12 dholland
810 1.12 dholland /*
811 1.12 dholland * Integers
812 1.12 dholland */
813 1.12 dholland for (i=0; i<num_save_ints; i++) {
814 1.12 dholland if (savefile_cread(sf, &val, sizeof(val))) {
815 1.12 dholland savefile_close(sf);
816 1.12 dholland return 1;
817 1.12 dholland }
818 1.12 dholland val = ntohl(val);
819 1.12 dholland *(save_ints[i]) = val;
820 1.12 dholland }
821 1.12 dholland
822 1.12 dholland /*
823 1.12 dholland * Arrays of integers
824 1.12 dholland */
825 1.12 dholland for (i=0; i<num_save_intarrays; i++) {
826 1.12 dholland n = save_intarrays[i].num;
827 1.12 dholland for (j=0; j<n; j++) {
828 1.12 dholland if (savefile_cread(sf, &val, sizeof(val))) {
829 1.12 dholland savefile_close(sf);
830 1.12 dholland return 1;
831 1.12 dholland }
832 1.12 dholland val = ntohl(val);
833 1.12 dholland save_intarrays[i].ptr[j] = val;
834 1.12 dholland }
835 1.12 dholland }
836 1.12 dholland
837 1.12 dholland #if 0
838 1.12 dholland /*
839 1.12 dholland * Blobs
840 1.12 dholland */
841 1.12 dholland for (i=0; i<num_save_blobs; i++) {
842 1.12 dholland if (savefile_cread(sf, save_blobs[i].ptr, save_blobs[i].len)) {
843 1.12 dholland savefile_close(sf);
844 1.12 dholland return 1;
845 1.12 dholland }
846 1.12 dholland }
847 1.12 dholland #endif
848 1.12 dholland
849 1.12 dholland if (savefile_binread(sf, &sum, sizeof(&sum))) {
850 1.12 dholland savefile_close(sf);
851 1.12 dholland return 1;
852 1.12 dholland }
853 1.12 dholland sum = ntohl(sum);
854 1.12 dholland /* See if she cheated */
855 1.13 dholland if (!skipsum && sum != crc_get(&sf->crc)) {
856 1.12 dholland /* Tsk tsk, altered the file */
857 1.12 dholland savefile_close(sf);
858 1.12 dholland return 2;
859 1.12 dholland }
860 1.12 dholland savefile_close(sf);
861 1.12 dholland
862 1.12 dholland /* Load theoretically invalidates these */
863 1.12 dholland tkk = NULL;
864 1.12 dholland wd1 = NULL;
865 1.12 dholland wd2 = NULL;
866 1.12 dholland
867 1.1 jtc return 0;
868 1.1 jtc }
869