1*18bae931Smrg /* $NetBSD: save.c,v 1.15 2021/04/12 09:12:28 mrg Exp $ */
243b95251Scgd
3107c0a28Sjtc /*-
4107c0a28Sjtc * Copyright (c) 1991, 1993
5107c0a28Sjtc * The Regents of the University of California. All rights reserved.
6107c0a28Sjtc *
7107c0a28Sjtc * The game adventure was originally written in Fortran by Will Crowther
8107c0a28Sjtc * and Don Woods. It was later translated to C and enhanced by Jim
9107c0a28Sjtc * Gillogly. This code is derived from software contributed to Berkeley
10107c0a28Sjtc * by Jim Gillogly at The Rand Corporation.
11107c0a28Sjtc *
12107c0a28Sjtc * Redistribution and use in source and binary forms, with or without
13107c0a28Sjtc * modification, are permitted provided that the following conditions
14107c0a28Sjtc * are met:
15107c0a28Sjtc * 1. Redistributions of source code must retain the above copyright
16107c0a28Sjtc * notice, this list of conditions and the following disclaimer.
17107c0a28Sjtc * 2. Redistributions in binary form must reproduce the above copyright
18107c0a28Sjtc * notice, this list of conditions and the following disclaimer in the
19107c0a28Sjtc * documentation and/or other materials provided with the distribution.
20e5aeb4eaSagc * 3. Neither the name of the University nor the names of its contributors
21107c0a28Sjtc * may be used to endorse or promote products derived from this software
22107c0a28Sjtc * without specific prior written permission.
23107c0a28Sjtc *
24107c0a28Sjtc * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
25107c0a28Sjtc * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26107c0a28Sjtc * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27107c0a28Sjtc * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28107c0a28Sjtc * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29107c0a28Sjtc * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30107c0a28Sjtc * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31107c0a28Sjtc * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32107c0a28Sjtc * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33107c0a28Sjtc * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34107c0a28Sjtc * SUCH DAMAGE.
35107c0a28Sjtc */
36107c0a28Sjtc
37690bee10Schristos #include <sys/cdefs.h>
38107c0a28Sjtc #ifndef lint
3943b95251Scgd #if 0
40107c0a28Sjtc static char sccsid[] = "@(#)save.c 8.1 (Berkeley) 5/31/93";
4143b95251Scgd #else
42*18bae931Smrg __RCSID("$NetBSD: save.c,v 1.15 2021/04/12 09:12:28 mrg Exp $");
4343b95251Scgd #endif
44107c0a28Sjtc #endif /* not lint */
45107c0a28Sjtc
46c99eaa0aSdholland #include <sys/types.h>
47c99eaa0aSdholland #include <sys/time.h>
48c99eaa0aSdholland #include <stdbool.h>
49107c0a28Sjtc #include <stdio.h>
50690bee10Schristos #include <stdlib.h>
51c99eaa0aSdholland #include <err.h>
52c99eaa0aSdholland #include <assert.h>
53c99eaa0aSdholland
54107c0a28Sjtc #include "hdr.h"
55690bee10Schristos #include "extern.h"
56107c0a28Sjtc
57c99eaa0aSdholland struct savefile {
58c99eaa0aSdholland FILE *f;
59c99eaa0aSdholland const char *name;
60c99eaa0aSdholland bool warned;
616f629695Sdholland size_t bintextpos;
62c99eaa0aSdholland uint32_t key;
63158b7407Sdholland struct crcstate crc;
64c99eaa0aSdholland unsigned char pad[8];
65c99eaa0aSdholland unsigned padpos;
66c99eaa0aSdholland };
67c99eaa0aSdholland
68c99eaa0aSdholland #define BINTEXT_WIDTH 60
69158b7407Sdholland #define FORMAT_VERSION 2
70158b7407Sdholland #define FORMAT_VERSION_NOSUM 1
71c99eaa0aSdholland static const char header[] = "Adventure save file\n";
72c99eaa0aSdholland
73c99eaa0aSdholland ////////////////////////////////////////////////////////////
74c99eaa0aSdholland // base16 output encoding
75c99eaa0aSdholland
76c99eaa0aSdholland /*
77c99eaa0aSdholland * Map 16 plain values into 90 coded values and back.
78c99eaa0aSdholland */
79c99eaa0aSdholland
80*18bae931Smrg static const char coding[91] =
81c99eaa0aSdholland "Db.GOyT]7a6zpF(c*5H9oK~0[WVAg&kR)ml,2^q-1Y3v+"
82c99eaa0aSdholland "X/=JirZL$C>_N?:}B{dfnsxU<@MQ%8|P!4h`ESt;euwIj"
83c99eaa0aSdholland ;
84c99eaa0aSdholland
85c99eaa0aSdholland static int
readletter(char letter,unsigned char * ret)86c99eaa0aSdholland readletter(char letter, unsigned char *ret)
87c99eaa0aSdholland {
88c99eaa0aSdholland const char *s;
89c99eaa0aSdholland
90c99eaa0aSdholland s = strchr(coding, letter);
91c99eaa0aSdholland if (s == NULL) {
92c99eaa0aSdholland return 1;
93c99eaa0aSdholland }
94c99eaa0aSdholland *ret = (s - coding) % 16;
95c99eaa0aSdholland return 0;
96c99eaa0aSdholland }
97c99eaa0aSdholland
98c99eaa0aSdholland static char
writeletter(unsigned char nibble)99c99eaa0aSdholland writeletter(unsigned char nibble)
100c99eaa0aSdholland {
101c99eaa0aSdholland unsigned code;
102c99eaa0aSdholland
103c99eaa0aSdholland assert(nibble < 16);
104c99eaa0aSdholland do {
105c99eaa0aSdholland code = (16 * (random() % 6)) + nibble;
106c99eaa0aSdholland } while (code >= 90);
107c99eaa0aSdholland return coding[code];
108c99eaa0aSdholland }
109c99eaa0aSdholland
110c99eaa0aSdholland ////////////////////////////////////////////////////////////
111c99eaa0aSdholland // savefile
112c99eaa0aSdholland
113c99eaa0aSdholland /*
114c99eaa0aSdholland * Open a savefile.
115c99eaa0aSdholland */
116c99eaa0aSdholland static struct savefile *
savefile_open(const char * name,bool forwrite)117c99eaa0aSdholland savefile_open(const char *name, bool forwrite)
118c99eaa0aSdholland {
119c99eaa0aSdholland struct savefile *sf;
120c99eaa0aSdholland
121c99eaa0aSdholland sf = malloc(sizeof(*sf));
122c99eaa0aSdholland if (sf == NULL) {
123c99eaa0aSdholland return NULL;
124c99eaa0aSdholland }
125c99eaa0aSdholland sf->f = fopen(name, forwrite ? "w" : "r");
126c99eaa0aSdholland if (sf->f == NULL) {
127c99eaa0aSdholland free(sf);
128c99eaa0aSdholland fprintf(stderr,
129c99eaa0aSdholland "Hmm. The name \"%s\" appears to be magically blocked.\n",
130c99eaa0aSdholland name);
131c99eaa0aSdholland return NULL;
132c99eaa0aSdholland }
133c99eaa0aSdholland sf->name = name;
134c99eaa0aSdholland sf->warned = false;
135c99eaa0aSdholland sf->bintextpos = 0;
136c99eaa0aSdholland sf->key = 0;
137158b7407Sdholland crc_start(&sf->crc);
138c99eaa0aSdholland memset(sf->pad, 0, sizeof(sf->pad));
139c99eaa0aSdholland sf->padpos = 0;
140c99eaa0aSdholland return sf;
141c99eaa0aSdholland }
142c99eaa0aSdholland
143c99eaa0aSdholland /*
144c99eaa0aSdholland * Raw read.
145c99eaa0aSdholland */
146c99eaa0aSdholland static int
savefile_rawread(struct savefile * sf,void * data,size_t len)147c99eaa0aSdholland savefile_rawread(struct savefile *sf, void *data, size_t len)
148c99eaa0aSdholland {
149c99eaa0aSdholland size_t result;
150c99eaa0aSdholland
151c99eaa0aSdholland result = fread(data, 1, len, sf->f);
152c99eaa0aSdholland if (result != len || ferror(sf->f)) {
153c99eaa0aSdholland fprintf(stderr, "Oops: error reading %s.\n", sf->name);
154c99eaa0aSdholland sf->warned = true;
155c99eaa0aSdholland return 1;
156c99eaa0aSdholland }
157c99eaa0aSdholland return 0;
158c99eaa0aSdholland }
159c99eaa0aSdholland
160c99eaa0aSdholland /*
161c99eaa0aSdholland * Raw write.
162c99eaa0aSdholland */
163c99eaa0aSdholland static int
savefile_rawwrite(struct savefile * sf,const void * data,size_t len)164c99eaa0aSdholland savefile_rawwrite(struct savefile *sf, const void *data, size_t len)
165c99eaa0aSdholland {
166c99eaa0aSdholland size_t result;
167c99eaa0aSdholland
168c99eaa0aSdholland result = fwrite(data, 1, len, sf->f);
169c99eaa0aSdholland if (result != len || ferror(sf->f)) {
170c99eaa0aSdholland fprintf(stderr, "Oops: error writing %s.\n", sf->name);
171c99eaa0aSdholland sf->warned = true;
172c99eaa0aSdholland return 1;
173c99eaa0aSdholland }
174c99eaa0aSdholland return 0;
175c99eaa0aSdholland }
176c99eaa0aSdholland
177c99eaa0aSdholland /*
178c99eaa0aSdholland * Close a savefile.
179c99eaa0aSdholland */
180c99eaa0aSdholland static int
savefile_close(struct savefile * sf)181c99eaa0aSdholland savefile_close(struct savefile *sf)
182c99eaa0aSdholland {
183c99eaa0aSdholland int ret;
184c99eaa0aSdholland
185c99eaa0aSdholland if (sf->bintextpos > 0) {
186c99eaa0aSdholland savefile_rawwrite(sf, "\n", 1);
187c99eaa0aSdholland }
188c99eaa0aSdholland
189c99eaa0aSdholland ret = 0;
190c99eaa0aSdholland if (fclose(sf->f)) {
191c99eaa0aSdholland if (!sf->warned) {
192c99eaa0aSdholland fprintf(stderr, "Oops: error on %s.\n", sf->name);
193c99eaa0aSdholland }
194c99eaa0aSdholland ret = 1;
195c99eaa0aSdholland }
196c99eaa0aSdholland free(sf);
197c99eaa0aSdholland return ret;
198c99eaa0aSdholland }
199c99eaa0aSdholland
200c99eaa0aSdholland /*
201c99eaa0aSdholland * Read encoded binary data, discarding any whitespace that appears.
202c99eaa0aSdholland */
203c99eaa0aSdholland static int
savefile_bintextread(struct savefile * sf,void * data,size_t len)204c99eaa0aSdholland savefile_bintextread(struct savefile *sf, void *data, size_t len)
205c99eaa0aSdholland {
206c99eaa0aSdholland size_t pos;
207c99eaa0aSdholland unsigned char *udata;
208c99eaa0aSdholland int ch;
209c99eaa0aSdholland
210c99eaa0aSdholland udata = data;
211c99eaa0aSdholland pos = 0;
212c99eaa0aSdholland while (pos < len) {
213c99eaa0aSdholland ch = fgetc(sf->f);
214c99eaa0aSdholland if (ch == EOF || ferror(sf->f)) {
215c99eaa0aSdholland fprintf(stderr, "Oops: error reading %s.\n", sf->name);
216c99eaa0aSdholland sf->warned = true;
217c99eaa0aSdholland return 1;
218c99eaa0aSdholland }
219c99eaa0aSdholland if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') {
220c99eaa0aSdholland continue;
221c99eaa0aSdholland }
222c99eaa0aSdholland udata[pos++] = ch;
223c99eaa0aSdholland }
224c99eaa0aSdholland return 0;
225c99eaa0aSdholland }
226c99eaa0aSdholland
227c99eaa0aSdholland /*
228c99eaa0aSdholland * Read binary data, decoding from text using readletter().
229c99eaa0aSdholland */
230c99eaa0aSdholland static int
savefile_binread(struct savefile * sf,void * data,size_t len)231c99eaa0aSdholland savefile_binread(struct savefile *sf, void *data, size_t len)
232c99eaa0aSdholland {
233c99eaa0aSdholland unsigned char buf[64];
234c99eaa0aSdholland unsigned char *udata;
235c99eaa0aSdholland unsigned char val1, val2;
236c99eaa0aSdholland size_t pos, amt, i;
237c99eaa0aSdholland
238c99eaa0aSdholland udata = data;
239c99eaa0aSdholland pos = 0;
240c99eaa0aSdholland while (pos < len) {
241c99eaa0aSdholland amt = len - pos;
242c99eaa0aSdholland if (amt > sizeof(buf) / 2) {
243c99eaa0aSdholland amt = sizeof(buf) / 2;
244c99eaa0aSdholland }
245c99eaa0aSdholland if (savefile_bintextread(sf, buf, amt*2)) {
246c99eaa0aSdholland return 1;
247c99eaa0aSdholland }
248c99eaa0aSdholland for (i=0; i<amt; i++) {
249c99eaa0aSdholland if (readletter(buf[i*2], &val1)) {
250c99eaa0aSdholland return 1;
251c99eaa0aSdholland }
252c99eaa0aSdholland if (readletter(buf[i*2 + 1], &val2)) {
253c99eaa0aSdholland return 1;
254c99eaa0aSdholland }
255c99eaa0aSdholland udata[pos++] = val1 * 16 + val2;
256c99eaa0aSdholland }
257c99eaa0aSdholland }
258c99eaa0aSdholland return 0;
259c99eaa0aSdholland }
260c99eaa0aSdholland
261c99eaa0aSdholland /*
262c99eaa0aSdholland * Write encoded binary data, inserting newlines to get a neatly
263c99eaa0aSdholland * formatted block.
264c99eaa0aSdholland */
265c99eaa0aSdholland static int
savefile_bintextwrite(struct savefile * sf,const void * data,size_t len)266c99eaa0aSdholland savefile_bintextwrite(struct savefile *sf, const void *data, size_t len)
267c99eaa0aSdholland {
268c99eaa0aSdholland size_t pos, amt;
269c99eaa0aSdholland const unsigned char *udata;
270c99eaa0aSdholland
271c99eaa0aSdholland udata = data;
272c99eaa0aSdholland pos = 0;
273c99eaa0aSdholland while (pos < len) {
274c99eaa0aSdholland amt = BINTEXT_WIDTH - sf->bintextpos;
275c99eaa0aSdholland if (amt > len - pos) {
276c99eaa0aSdholland amt = len - pos;
277c99eaa0aSdholland }
278c99eaa0aSdholland if (savefile_rawwrite(sf, udata + pos, amt)) {
279c99eaa0aSdholland return 1;
280c99eaa0aSdholland }
281c99eaa0aSdholland pos += amt;
282c99eaa0aSdholland sf->bintextpos += amt;
283c99eaa0aSdholland if (sf->bintextpos >= BINTEXT_WIDTH) {
284c99eaa0aSdholland savefile_rawwrite(sf, "\n", 1);
285c99eaa0aSdholland sf->bintextpos = 0;
286c99eaa0aSdholland }
287c99eaa0aSdholland }
288c99eaa0aSdholland return 0;
289c99eaa0aSdholland }
290c99eaa0aSdholland
291c99eaa0aSdholland /*
292c99eaa0aSdholland * Write binary data, encoding as text using writeletter().
293c99eaa0aSdholland */
294c99eaa0aSdholland static int
savefile_binwrite(struct savefile * sf,const void * data,size_t len)295c99eaa0aSdholland savefile_binwrite(struct savefile *sf, const void *data, size_t len)
296c99eaa0aSdholland {
297c99eaa0aSdholland unsigned char buf[64];
298c99eaa0aSdholland const unsigned char *udata;
299c99eaa0aSdholland size_t pos, bpos;
300c99eaa0aSdholland unsigned char byte;
301c99eaa0aSdholland
302c99eaa0aSdholland udata = data;
303c99eaa0aSdholland pos = 0;
304c99eaa0aSdholland bpos = 0;
305c99eaa0aSdholland while (pos < len) {
306c99eaa0aSdholland byte = udata[pos++];
307c99eaa0aSdholland buf[bpos++] = writeletter(byte >> 4);
308c99eaa0aSdholland buf[bpos++] = writeletter(byte & 0xf);
309c99eaa0aSdholland if (bpos >= sizeof(buf)) {
310c99eaa0aSdholland if (savefile_bintextwrite(sf, buf, bpos)) {
311c99eaa0aSdholland return 1;
312c99eaa0aSdholland }
313c99eaa0aSdholland bpos = 0;
314c99eaa0aSdholland }
315c99eaa0aSdholland }
316c99eaa0aSdholland if (savefile_bintextwrite(sf, buf, bpos)) {
317c99eaa0aSdholland return 1;
318c99eaa0aSdholland }
319c99eaa0aSdholland return 0;
320c99eaa0aSdholland }
321c99eaa0aSdholland
322c99eaa0aSdholland /*
323c99eaa0aSdholland * Lightweight "encryption" for save files. This is not meant to
324c99eaa0aSdholland * be secure and wouldn't be even if we didn't write the decrypt
325c99eaa0aSdholland * key to the beginning of the save file; it's just meant to be
326c99eaa0aSdholland * enough to discourage casual cheating.
327c99eaa0aSdholland */
328c99eaa0aSdholland
329c99eaa0aSdholland /*
330c99eaa0aSdholland * Make cheesy hash of buf[0..buflen]. Note: buf and outhash may overlap.
331c99eaa0aSdholland */
332c99eaa0aSdholland static void
hash(const void * data,size_t datalen,unsigned char * out,size_t outlen)333c99eaa0aSdholland hash(const void *data, size_t datalen, unsigned char *out, size_t outlen)
334c99eaa0aSdholland {
335c99eaa0aSdholland const unsigned char *udata;
336c99eaa0aSdholland size_t i;
337c99eaa0aSdholland uint64_t val;
338c99eaa0aSdholland const unsigned char *uval;
339c99eaa0aSdholland size_t valpos;
340c99eaa0aSdholland
341c99eaa0aSdholland udata = data;
342c99eaa0aSdholland val = 0;
343c99eaa0aSdholland for (i=0; i<datalen; i++) {
344c99eaa0aSdholland val = val ^ 0xbadc0ffee;
345c99eaa0aSdholland val = (val << 4) | (val >> 60);
3466f629695Sdholland val += udata[i] ^ 0xbeefU;
347c99eaa0aSdholland }
348c99eaa0aSdholland
349c99eaa0aSdholland uval = (unsigned char *)&val;
350c99eaa0aSdholland valpos = 0;
351c99eaa0aSdholland for (i=0; i<outlen; i++) {
352c99eaa0aSdholland out[i] = uval[valpos++];
353c99eaa0aSdholland if (valpos >= sizeof(val)) {
354c99eaa0aSdholland valpos = 0;
355c99eaa0aSdholland }
356c99eaa0aSdholland }
357c99eaa0aSdholland }
358c99eaa0aSdholland
359c99eaa0aSdholland /*
360c99eaa0aSdholland * Set the "encryption" key.
361c99eaa0aSdholland */
362c99eaa0aSdholland static void
savefile_key(struct savefile * sf,uint32_t key)363c99eaa0aSdholland savefile_key(struct savefile *sf, uint32_t key)
364c99eaa0aSdholland {
365c99eaa0aSdholland sf->key = 0;
366158b7407Sdholland crc_start(&sf->crc);
367c99eaa0aSdholland hash(&sf->key, sizeof(sf->key), sf->pad, sizeof(sf->pad));
368c99eaa0aSdholland sf->padpos = 0;
369c99eaa0aSdholland }
370c99eaa0aSdholland
371c99eaa0aSdholland /*
372c99eaa0aSdholland * Get an "encryption" pad byte. This forms a stream "cipher" that we
373c99eaa0aSdholland * xor with the plaintext save data.
374c99eaa0aSdholland */
375c99eaa0aSdholland static unsigned char
savefile_getpad(struct savefile * sf)376c99eaa0aSdholland savefile_getpad(struct savefile *sf)
377c99eaa0aSdholland {
378c99eaa0aSdholland unsigned char ret;
379c99eaa0aSdholland
380c99eaa0aSdholland ret = sf->pad[sf->padpos++];
381c99eaa0aSdholland if (sf->padpos >= sizeof(sf->pad)) {
382c99eaa0aSdholland hash(sf->pad, sizeof(sf->pad), sf->pad, sizeof(sf->pad));
383c99eaa0aSdholland sf->padpos = 0;
384c99eaa0aSdholland }
385c99eaa0aSdholland return ret;
386c99eaa0aSdholland }
387c99eaa0aSdholland
388c99eaa0aSdholland /*
389c99eaa0aSdholland * Read "encrypted" data.
390c99eaa0aSdholland */
391c99eaa0aSdholland static int
savefile_cread(struct savefile * sf,void * data,size_t len)392c99eaa0aSdholland savefile_cread(struct savefile *sf, void *data, size_t len)
393c99eaa0aSdholland {
394c99eaa0aSdholland char buf[64];
395c99eaa0aSdholland unsigned char *udata;
396c99eaa0aSdholland size_t pos, amt, i;
397c99eaa0aSdholland unsigned char ch;
398c99eaa0aSdholland
399c99eaa0aSdholland udata = data;
400c99eaa0aSdholland pos = 0;
401c99eaa0aSdholland while (pos < len) {
402c99eaa0aSdholland amt = len - pos;
403c99eaa0aSdholland if (amt > sizeof(buf)) {
404c99eaa0aSdholland amt = sizeof(buf);
405c99eaa0aSdholland }
406c99eaa0aSdholland if (savefile_binread(sf, buf, amt)) {
407c99eaa0aSdholland return 1;
408c99eaa0aSdholland }
409c99eaa0aSdholland for (i=0; i<amt; i++) {
410c99eaa0aSdholland ch = buf[i];
411c99eaa0aSdholland ch ^= savefile_getpad(sf);
412c99eaa0aSdholland udata[pos + i] = ch;
413c99eaa0aSdholland }
414c99eaa0aSdholland pos += amt;
415c99eaa0aSdholland }
416158b7407Sdholland crc_add(&sf->crc, data, len);
417c99eaa0aSdholland return 0;
418c99eaa0aSdholland }
419c99eaa0aSdholland
420c99eaa0aSdholland /*
421c99eaa0aSdholland * Write "encrypted" data.
422c99eaa0aSdholland */
423c99eaa0aSdholland static int
savefile_cwrite(struct savefile * sf,const void * data,size_t len)424c99eaa0aSdholland savefile_cwrite(struct savefile *sf, const void *data, size_t len)
425c99eaa0aSdholland {
426c99eaa0aSdholland char buf[64];
427c99eaa0aSdholland const unsigned char *udata;
428c99eaa0aSdholland size_t pos, amt, i;
429c99eaa0aSdholland unsigned char ch;
430c99eaa0aSdholland
431c99eaa0aSdholland udata = data;
432c99eaa0aSdholland pos = 0;
433c99eaa0aSdholland while (pos < len) {
434c99eaa0aSdholland amt = len - pos;
435c99eaa0aSdholland if (amt > sizeof(buf)) {
436c99eaa0aSdholland amt = sizeof(buf);
437c99eaa0aSdholland }
438c99eaa0aSdholland for (i=0; i<amt; i++) {
439c99eaa0aSdholland ch = udata[pos + i];
440c99eaa0aSdholland ch ^= savefile_getpad(sf);
441c99eaa0aSdholland buf[i] = ch;
442c99eaa0aSdholland }
443c99eaa0aSdholland if (savefile_binwrite(sf, buf, amt)) {
444c99eaa0aSdholland return 1;
445c99eaa0aSdholland }
446c99eaa0aSdholland pos += amt;
447c99eaa0aSdholland }
448158b7407Sdholland crc_add(&sf->crc, data, len);
449c99eaa0aSdholland return 0;
450c99eaa0aSdholland }
451c99eaa0aSdholland
452c99eaa0aSdholland ////////////////////////////////////////////////////////////
453c99eaa0aSdholland // compat for old save files
454c99eaa0aSdholland
455c99eaa0aSdholland struct compat_saveinfo {
456107c0a28Sjtc void *address;
4576f629695Sdholland size_t width;
458107c0a28Sjtc };
459107c0a28Sjtc
460c99eaa0aSdholland static const struct compat_saveinfo compat_savearray[] =
461107c0a28Sjtc {
462690bee10Schristos {&abbnum, sizeof(abbnum)},
463690bee10Schristos {&attack, sizeof(attack)},
464690bee10Schristos {&blklin, sizeof(blklin)},
465690bee10Schristos {&bonus, sizeof(bonus)},
466690bee10Schristos {&chloc, sizeof(chloc)},
467690bee10Schristos {&chloc2, sizeof(chloc2)},
468690bee10Schristos {&clock1, sizeof(clock1)},
469690bee10Schristos {&clock2, sizeof(clock2)},
470690bee10Schristos {&closed, sizeof(closed)},
471aa19a9d0Sdholland {&isclosing, sizeof(isclosing)},
472aa19a9d0Sdholland {&daltloc, sizeof(daltloc)},
473690bee10Schristos {&demo, sizeof(demo)},
474690bee10Schristos {&detail, sizeof(detail)},
475690bee10Schristos {&dflag, sizeof(dflag)},
476690bee10Schristos {&dkill, sizeof(dkill)},
477690bee10Schristos {&dtotal, sizeof(dtotal)},
478690bee10Schristos {&foobar, sizeof(foobar)},
479690bee10Schristos {&gaveup, sizeof(gaveup)},
480aa19a9d0Sdholland {&holding, sizeof(holding)},
481690bee10Schristos {&iwest, sizeof(iwest)},
482690bee10Schristos {&k, sizeof(k)},
483690bee10Schristos {&k2, sizeof(k2)},
484690bee10Schristos {&knfloc, sizeof(knfloc)},
485690bee10Schristos {&kq, sizeof(kq)},
486aa19a9d0Sdholland {&latency, sizeof(latency)},
487690bee10Schristos {&limit, sizeof(limit)},
488690bee10Schristos {&lmwarn, sizeof(lmwarn)},
489690bee10Schristos {&loc, sizeof(loc)},
490690bee10Schristos {&maxdie, sizeof(maxdie)},
491aa19a9d0Sdholland {&maxscore, sizeof(maxscore)},
492690bee10Schristos {&newloc, sizeof(newloc)},
493690bee10Schristos {&numdie, sizeof(numdie)},
494690bee10Schristos {&obj, sizeof(obj)},
495aa19a9d0Sdholland {&oldloc2, sizeof(oldloc2)},
496690bee10Schristos {&oldloc, sizeof(oldloc)},
497690bee10Schristos {&panic, sizeof(panic)},
498adf74b1eShubertf {&saveday, sizeof(saveday)},
499690bee10Schristos {&savet, sizeof(savet)},
500aa19a9d0Sdholland {&scoring, sizeof(scoring)},
501690bee10Schristos {&spk, sizeof(spk)},
502690bee10Schristos {&stick, sizeof(stick)},
503690bee10Schristos {&tally, sizeof(tally)},
504690bee10Schristos {&tally2, sizeof(tally2)},
505690bee10Schristos {&tkk, sizeof(tkk)},
506690bee10Schristos {&turns, sizeof(turns)},
507690bee10Schristos {&verb, sizeof(verb)},
508690bee10Schristos {&wd1, sizeof(wd1)},
509690bee10Schristos {&wd2, sizeof(wd2)},
510aa19a9d0Sdholland {&wasdark, sizeof(wasdark)},
511690bee10Schristos {&yea, sizeof(yea)},
512690bee10Schristos {atloc, sizeof(atloc)},
513690bee10Schristos {dloc, sizeof(dloc)},
514690bee10Schristos {dseen, sizeof(dseen)},
515690bee10Schristos {fixed, sizeof(fixed)},
516690bee10Schristos {hinted, sizeof(hinted)},
517690bee10Schristos {links, sizeof(links)},
518690bee10Schristos {odloc, sizeof(odloc)},
519690bee10Schristos {place, sizeof(place)},
520690bee10Schristos {prop, sizeof(prop)},
521690bee10Schristos {tk, sizeof(tk)},
522107c0a28Sjtc
523690bee10Schristos {NULL, 0}
524107c0a28Sjtc };
525107c0a28Sjtc
526c99eaa0aSdholland static int
compat_restore(const char * infile)527c99eaa0aSdholland compat_restore(const char *infile)
528107c0a28Sjtc {
529107c0a28Sjtc FILE *in;
530c99eaa0aSdholland const struct compat_saveinfo *p;
531107c0a28Sjtc char *s;
532690bee10Schristos long sum, cksum = 0;
5336f629695Sdholland size_t i;
534158b7407Sdholland struct crcstate crc;
535107c0a28Sjtc
53684f80fd6Slukem if ((in = fopen(infile, "rb")) == NULL) {
537107c0a28Sjtc fprintf(stderr,
538107c0a28Sjtc "Hmm. The file \"%s\" appears to be magically blocked.\n",
539107c0a28Sjtc infile);
540107c0a28Sjtc return 1;
541107c0a28Sjtc }
542107c0a28Sjtc fread(&sum, sizeof(sum), 1, in); /* Get the seed */
543107c0a28Sjtc srandom((int) sum);
544c99eaa0aSdholland for (p = compat_savearray; p->address != NULL; p++) {
545107c0a28Sjtc fread(p->address, p->width, 1, in);
546107c0a28Sjtc for (s = p->address, i = 0; i < p->width; i++, s++)
547107c0a28Sjtc *s = (*s ^ random()) & 0xFF; /* Lightly decrypt */
548107c0a28Sjtc }
549107c0a28Sjtc fclose(in);
550107c0a28Sjtc
551158b7407Sdholland crc_start(&crc); /* See if she cheated */
552c99eaa0aSdholland for (p = compat_savearray; p->address != NULL; p++)
553158b7407Sdholland crc_add(&crc, p->address, p->width);
554158b7407Sdholland cksum = crc_get(&crc);
555107c0a28Sjtc if (sum != cksum) /* Tsk tsk */
556107c0a28Sjtc return 2; /* Altered the file */
557107c0a28Sjtc /* We successfully restored, so this really was a save file */
558c99eaa0aSdholland
559c99eaa0aSdholland /*
560c99eaa0aSdholland * The above code loads these from disk even though they're
561c99eaa0aSdholland * pointers. Null them out and hope we don't crash on them
562c99eaa0aSdholland * later; that's better than having them be garbage.
563c99eaa0aSdholland */
564c99eaa0aSdholland tkk = NULL;
565c99eaa0aSdholland wd1 = NULL;
566c99eaa0aSdholland wd2 = NULL;
567c99eaa0aSdholland
568c99eaa0aSdholland return 0;
569c99eaa0aSdholland }
570c99eaa0aSdholland
571c99eaa0aSdholland ////////////////////////////////////////////////////////////
572c99eaa0aSdholland // save + restore
573c99eaa0aSdholland
574c99eaa0aSdholland static int *const save_ints[] = {
575c99eaa0aSdholland &abbnum,
576c99eaa0aSdholland &attack,
577c99eaa0aSdholland &blklin,
578c99eaa0aSdholland &bonus,
579c99eaa0aSdholland &chloc,
580c99eaa0aSdholland &chloc2,
581c99eaa0aSdholland &clock1,
582c99eaa0aSdholland &clock2,
583c99eaa0aSdholland &closed,
584c99eaa0aSdholland &isclosing,
585c99eaa0aSdholland &daltloc,
586c99eaa0aSdholland &demo,
587c99eaa0aSdholland &detail,
588c99eaa0aSdholland &dflag,
589c99eaa0aSdholland &dkill,
590c99eaa0aSdholland &dtotal,
591c99eaa0aSdholland &foobar,
592c99eaa0aSdholland &gaveup,
593c99eaa0aSdholland &holding,
594c99eaa0aSdholland &iwest,
595c99eaa0aSdholland &k,
596c99eaa0aSdholland &k2,
597c99eaa0aSdholland &knfloc,
598c99eaa0aSdholland &kq,
599c99eaa0aSdholland &latency,
600c99eaa0aSdholland &limit,
601c99eaa0aSdholland &lmwarn,
602c99eaa0aSdholland &loc,
603c99eaa0aSdholland &maxdie,
604c99eaa0aSdholland &maxscore,
605c99eaa0aSdholland &newloc,
606c99eaa0aSdholland &numdie,
607c99eaa0aSdholland &obj,
608c99eaa0aSdholland &oldloc2,
609c99eaa0aSdholland &oldloc,
610c99eaa0aSdholland &panic,
611c99eaa0aSdholland &saveday,
612c99eaa0aSdholland &savet,
613c99eaa0aSdholland &scoring,
614c99eaa0aSdholland &spk,
615c99eaa0aSdholland &stick,
616c99eaa0aSdholland &tally,
617c99eaa0aSdholland &tally2,
618c99eaa0aSdholland &turns,
619c99eaa0aSdholland &verb,
620c99eaa0aSdholland &wasdark,
621c99eaa0aSdholland &yea,
622c99eaa0aSdholland };
623c99eaa0aSdholland static const unsigned num_save_ints = __arraycount(save_ints);
624c99eaa0aSdholland
625c99eaa0aSdholland #define INTARRAY(sym) { sym, __arraycount(sym) }
626c99eaa0aSdholland
627c99eaa0aSdholland static const struct {
628c99eaa0aSdholland int *ptr;
629c99eaa0aSdholland unsigned num;
630c99eaa0aSdholland } save_intarrays[] = {
631c99eaa0aSdholland INTARRAY(atloc),
632c99eaa0aSdholland INTARRAY(dseen),
633c99eaa0aSdholland INTARRAY(dloc),
634c99eaa0aSdholland INTARRAY(odloc),
635c99eaa0aSdholland INTARRAY(fixed),
636c99eaa0aSdholland INTARRAY(hinted),
637c99eaa0aSdholland INTARRAY(links),
638c99eaa0aSdholland INTARRAY(place),
639c99eaa0aSdholland INTARRAY(prop),
640c99eaa0aSdholland INTARRAY(tk),
641c99eaa0aSdholland };
642c99eaa0aSdholland static const unsigned num_save_intarrays = __arraycount(save_intarrays);
643c99eaa0aSdholland
644c99eaa0aSdholland #undef INTARRAY
645c99eaa0aSdholland
646c99eaa0aSdholland #if 0
647c99eaa0aSdholland static const struct {
648c99eaa0aSdholland void *ptr;
649c99eaa0aSdholland size_t len;
650c99eaa0aSdholland } save_blobs[] = {
651c99eaa0aSdholland { &wd1, sizeof(wd1) },
652c99eaa0aSdholland { &wd2, sizeof(wd2) },
653c99eaa0aSdholland { &tkk, sizeof(tkk) },
654c99eaa0aSdholland };
655c99eaa0aSdholland static const unsigned num_save_blobs = __arraycount(save_blobs);
656c99eaa0aSdholland #endif
657c99eaa0aSdholland
658c99eaa0aSdholland /*
659c99eaa0aSdholland * Write out a save file. Returns nonzero on error.
660c99eaa0aSdholland */
661c99eaa0aSdholland int
save(const char * outfile)662c99eaa0aSdholland save(const char *outfile)
663c99eaa0aSdholland {
664c99eaa0aSdholland struct savefile *sf;
665c99eaa0aSdholland struct timespec now;
666c99eaa0aSdholland uint32_t key, writeable_key;
667c99eaa0aSdholland uint32_t version;
668c99eaa0aSdholland unsigned i, j, n;
669158b7407Sdholland uint32_t val, sum;
670c99eaa0aSdholland
671c99eaa0aSdholland sf = savefile_open(outfile, true);
672c99eaa0aSdholland if (sf == NULL) {
673c99eaa0aSdholland return 1;
674c99eaa0aSdholland }
675c99eaa0aSdholland
676c99eaa0aSdholland if (savefile_rawwrite(sf, header, strlen(header))) {
677c99eaa0aSdholland savefile_close(sf);
678c99eaa0aSdholland return 1;
679c99eaa0aSdholland }
680c99eaa0aSdholland
681c99eaa0aSdholland version = htonl(FORMAT_VERSION);
682c99eaa0aSdholland if (savefile_binwrite(sf, &version, sizeof(version))) {
683c99eaa0aSdholland savefile_close(sf);
684c99eaa0aSdholland return 1;
685c99eaa0aSdholland }
686c99eaa0aSdholland
687c99eaa0aSdholland clock_gettime(CLOCK_REALTIME, &now);
688c99eaa0aSdholland key = (uint32_t)(now.tv_sec & 0xffffffff) ^ (uint32_t)(now.tv_nsec);
689c99eaa0aSdholland
690c99eaa0aSdholland writeable_key = htonl(key);
691c99eaa0aSdholland if (savefile_binwrite(sf, &writeable_key, sizeof(writeable_key))) {
692c99eaa0aSdholland savefile_close(sf);
693c99eaa0aSdholland return 1;
694c99eaa0aSdholland }
695c99eaa0aSdholland
696c99eaa0aSdholland /* other parts of the code may depend on us doing this here */
697c99eaa0aSdholland srandom(key);
698c99eaa0aSdholland
699c99eaa0aSdholland savefile_key(sf, key);
700c99eaa0aSdholland
701c99eaa0aSdholland /*
702c99eaa0aSdholland * Integers
703c99eaa0aSdholland */
704c99eaa0aSdholland for (i=0; i<num_save_ints; i++) {
705c99eaa0aSdholland val = *(save_ints[i]);
706c99eaa0aSdholland val = htonl(val);
707c99eaa0aSdholland if (savefile_cwrite(sf, &val, sizeof(val))) {
708c99eaa0aSdholland savefile_close(sf);
709c99eaa0aSdholland return 1;
710c99eaa0aSdholland }
711c99eaa0aSdholland }
712c99eaa0aSdholland
713c99eaa0aSdholland /*
714c99eaa0aSdholland * Arrays of integers
715c99eaa0aSdholland */
716c99eaa0aSdholland for (i=0; i<num_save_intarrays; i++) {
717c99eaa0aSdholland n = save_intarrays[i].num;
718c99eaa0aSdholland for (j=0; j<n; j++) {
719c99eaa0aSdholland val = save_intarrays[i].ptr[j];
720c99eaa0aSdholland val = htonl(val);
721c99eaa0aSdholland if (savefile_cwrite(sf, &val, sizeof(val))) {
722c99eaa0aSdholland savefile_close(sf);
723c99eaa0aSdholland return 1;
724c99eaa0aSdholland }
725c99eaa0aSdholland }
726c99eaa0aSdholland }
727c99eaa0aSdholland
728c99eaa0aSdholland #if 0
729c99eaa0aSdholland /*
730c99eaa0aSdholland * Blobs
731c99eaa0aSdholland */
732c99eaa0aSdholland for (i=0; i<num_save_blobs; i++) {
733c99eaa0aSdholland if (savefile_cwrite(sf, save_blobs[i].ptr, save_blobs[i].len)) {
734c99eaa0aSdholland savefile_close(sf);
735c99eaa0aSdholland return 1;
736c99eaa0aSdholland }
737c99eaa0aSdholland }
738c99eaa0aSdholland #endif
739c99eaa0aSdholland
740158b7407Sdholland sum = htonl(crc_get(&sf->crc));
741158b7407Sdholland if (savefile_binwrite(sf, &sum, sizeof(&sum))) {
742c99eaa0aSdholland savefile_close(sf);
743c99eaa0aSdholland return 1;
744c99eaa0aSdholland }
745c99eaa0aSdholland savefile_close(sf);
746c99eaa0aSdholland return 0;
747c99eaa0aSdholland }
748c99eaa0aSdholland
749c99eaa0aSdholland /*
750c99eaa0aSdholland * Read in a save file. Returns nonzero on error.
751c99eaa0aSdholland */
752c99eaa0aSdholland int
restore(const char * infile)753c99eaa0aSdholland restore(const char *infile)
754c99eaa0aSdholland {
755c99eaa0aSdholland struct savefile *sf;
756c99eaa0aSdholland char buf[sizeof(header)];
757c99eaa0aSdholland size_t headersize = strlen(header);
758c99eaa0aSdholland uint32_t version, key, sum;
759c99eaa0aSdholland unsigned i, j, n;
760c99eaa0aSdholland uint32_t val;
761158b7407Sdholland bool skipsum = false;
762c99eaa0aSdholland
763c99eaa0aSdholland sf = savefile_open(infile, false);
764c99eaa0aSdholland if (sf == NULL) {
765c99eaa0aSdholland return 1;
766c99eaa0aSdholland }
767c99eaa0aSdholland
768c99eaa0aSdholland if (savefile_rawread(sf, buf, headersize)) {
769c99eaa0aSdholland savefile_close(sf);
770c99eaa0aSdholland return 1;
771c99eaa0aSdholland }
772c99eaa0aSdholland buf[headersize] = 0;
773c99eaa0aSdholland if (strcmp(buf, header) != 0) {
774c99eaa0aSdholland savefile_close(sf);
775c99eaa0aSdholland fprintf(stderr, "Oh dear, that isn't one of my save files.\n");
776c99eaa0aSdholland fprintf(stderr,
777c99eaa0aSdholland "Trying the Olde Waye; this myte notte Worke.\n");
778c99eaa0aSdholland return compat_restore(infile);
779c99eaa0aSdholland }
780c99eaa0aSdholland
781c99eaa0aSdholland if (savefile_binread(sf, &version, sizeof(version))) {
782c99eaa0aSdholland savefile_close(sf);
783c99eaa0aSdholland return 1;
784c99eaa0aSdholland }
785c99eaa0aSdholland version = ntohl(version);
786158b7407Sdholland switch (version) {
787158b7407Sdholland case FORMAT_VERSION:
788158b7407Sdholland break;
789158b7407Sdholland case FORMAT_VERSION_NOSUM:
790158b7407Sdholland skipsum = true;
791158b7407Sdholland break;
792158b7407Sdholland default:
793c99eaa0aSdholland savefile_close(sf);
794c99eaa0aSdholland fprintf(stderr,
795c99eaa0aSdholland "Oh dear, that file must be from the future. I don't know"
796c99eaa0aSdholland " how to read it!\n");
797c99eaa0aSdholland return 1;
798c99eaa0aSdholland }
799c99eaa0aSdholland
800c99eaa0aSdholland if (savefile_binread(sf, &key, sizeof(key))) {
801c99eaa0aSdholland savefile_close(sf);
802c99eaa0aSdholland return 1;
803c99eaa0aSdholland }
804c99eaa0aSdholland key = ntohl(key);
805c99eaa0aSdholland savefile_key(sf, key);
806c99eaa0aSdholland
807c99eaa0aSdholland /* other parts of the code may depend on us doing this here */
808c99eaa0aSdholland srandom(key);
809c99eaa0aSdholland
810c99eaa0aSdholland /*
811c99eaa0aSdholland * Integers
812c99eaa0aSdholland */
813c99eaa0aSdholland for (i=0; i<num_save_ints; i++) {
814c99eaa0aSdholland if (savefile_cread(sf, &val, sizeof(val))) {
815c99eaa0aSdholland savefile_close(sf);
816c99eaa0aSdholland return 1;
817c99eaa0aSdholland }
818c99eaa0aSdholland val = ntohl(val);
819c99eaa0aSdholland *(save_ints[i]) = val;
820c99eaa0aSdholland }
821c99eaa0aSdholland
822c99eaa0aSdholland /*
823c99eaa0aSdholland * Arrays of integers
824c99eaa0aSdholland */
825c99eaa0aSdholland for (i=0; i<num_save_intarrays; i++) {
826c99eaa0aSdholland n = save_intarrays[i].num;
827c99eaa0aSdholland for (j=0; j<n; j++) {
828c99eaa0aSdholland if (savefile_cread(sf, &val, sizeof(val))) {
829c99eaa0aSdholland savefile_close(sf);
830c99eaa0aSdholland return 1;
831c99eaa0aSdholland }
832c99eaa0aSdholland val = ntohl(val);
833c99eaa0aSdholland save_intarrays[i].ptr[j] = val;
834c99eaa0aSdholland }
835c99eaa0aSdholland }
836c99eaa0aSdholland
837c99eaa0aSdholland #if 0
838c99eaa0aSdholland /*
839c99eaa0aSdholland * Blobs
840c99eaa0aSdholland */
841c99eaa0aSdholland for (i=0; i<num_save_blobs; i++) {
842c99eaa0aSdholland if (savefile_cread(sf, save_blobs[i].ptr, save_blobs[i].len)) {
843c99eaa0aSdholland savefile_close(sf);
844c99eaa0aSdholland return 1;
845c99eaa0aSdholland }
846c99eaa0aSdholland }
847c99eaa0aSdholland #endif
848c99eaa0aSdholland
849c99eaa0aSdholland if (savefile_binread(sf, &sum, sizeof(&sum))) {
850c99eaa0aSdholland savefile_close(sf);
851c99eaa0aSdholland return 1;
852c99eaa0aSdholland }
853c99eaa0aSdholland sum = ntohl(sum);
854c99eaa0aSdholland /* See if she cheated */
855158b7407Sdholland if (!skipsum && sum != crc_get(&sf->crc)) {
856c99eaa0aSdholland /* Tsk tsk, altered the file */
857c99eaa0aSdholland savefile_close(sf);
858c99eaa0aSdholland return 2;
859c99eaa0aSdholland }
860c99eaa0aSdholland savefile_close(sf);
861c99eaa0aSdholland
862c99eaa0aSdholland /* Load theoretically invalidates these */
863c99eaa0aSdholland tkk = NULL;
864c99eaa0aSdholland wd1 = NULL;
865c99eaa0aSdholland wd2 = NULL;
866c99eaa0aSdholland
867107c0a28Sjtc return 0;
868107c0a28Sjtc }
869