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