-
Notifications
You must be signed in to change notification settings - Fork 1
/
unslist.c
288 lines (257 loc) · 7.31 KB
/
unslist.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
/* unslist.c: Functions to manage the list of unsolvable levels.
*
* Copyright (C) 2001-2006 by Brian Raiter, under the GNU General Public
* License. No warranty. See COPYING for details.
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "defs.h"
#include "err.h"
#include "fileio.h"
#include "res.h"
#include "solution.h"
#include "unslist.h"
/* The information comprising one entry in the list of unsolvable
* levels.
*/
typedef struct unslistentry {
int setid; /* the ID of the level set's name */
int levelnum; /* the level's number */
int size; /* the levels data's compressed size */
unsigned long hashval; /* the levels data's hash value */
int note; /* the entry's annotation ID, if any */
} unslistentry;
/* The pool of strings. In here are stored the level set names and the
* annotations. The string IDs are simple offsets from the strings
* pointer.
*/
static int stringsused = 0;
static int stringsallocated = 0;
static char *strings = NULL;
/* The list of level set names for which unsolvable levels appear on
* the list. This list allows the program to quickly find the level
* set name's string ID.
*/
static int namescount = 0;
static int namesallocated = 0;
static int *names = NULL;
/* The list of unsolvable levels proper.
*/
static int listcount = 0;
static int listallocated = 0;
static unslistentry *unslist = NULL;
/*
* Managing the pool of strings.
*/
/* Turn a string ID back into a string pointer. (Note that a string ID
* of zero will always return a null string, assuming that
* storestring() has been called at least once.)
*/
#define getstring(id) (strings + (id))
/* Make a copy of a string and add it to the string pool. The new
* string's ID is returned.
*/
static int storestring(char const *str)
{
int len;
len = strlen(str) + 1;
if (stringsused + len > stringsallocated) {
stringsallocated = stringsallocated ? 2 * stringsallocated : 256;
xalloc(strings, stringsallocated);
if (!stringsused) {
*strings = '\0';
++stringsused;
}
}
memcpy(strings + stringsused, str, len);
stringsused += len;
return stringsused - len;
}
/*
* Managing the list of set names.
*/
/* Return the string ID of the given set name. If the set name is not
* already in the list, then if add is TRUE the set name is added to
* the list; otherwise zero is returned.
*/
static int lookupsetname(char const *name, int add)
{
int i;
for (i = 0 ; i < namescount ; ++i)
if (!strcmp(getstring(names[i]), name))
return names[i];
if (!add)
return 0;
if (namescount >= namesallocated) {
namesallocated = namesallocated ? 2 * namesallocated : 8;
xalloc(names, namesallocated * sizeof *names);
}
names[namescount] = storestring(name);
return names[namescount++];
}
/*
* Managing the list of unsolvable levels.
*/
/* Add a new entry with the given data to the list.
*/
static int addtounslist(int setid, int levelnum,
int size, unsigned long hashval, int note)
{
if (listcount == listallocated) {
listallocated = listallocated ? listallocated * 2 : 16;
xalloc(unslist, listallocated * sizeof *unslist);
}
unslist[listcount].setid = setid;
unslist[listcount].levelnum = levelnum;
unslist[listcount].size = size;
unslist[listcount].hashval = hashval;
unslist[listcount].note = note;
++listcount;
return TRUE;
}
/* Remove all entries for the given level from the list. FALSE is
* returned if the level was not on the list to begin with.
*/
static int removefromunslist(int setid, int levelnum)
{
int i, f = FALSE;
for (i = 0 ; i < listcount ; ++i) {
if (unslist[i].setid == setid && unslist[i].levelnum == levelnum) {
--listcount;
unslist[i] = unslist[listcount];
f = TRUE;
}
}
return f;
}
/* Add the information in the given file to the list of unsolvable
* levels. Errors in the file are flagged but do not prevent the
* function from reading the rest of the file.
*/
static int readunslist(fileinfo *file)
{
char buf[256], token[256];
char const *p;
unsigned long hashval;
int setid, size;
int lineno, levelnum, n;
setid = 0;
for (lineno = 1 ; ; ++lineno) {
n = sizeof buf - 1;
if (!filegetline(file, buf, &n, NULL))
break;
for (p = buf ; isspace(*p) ; ++p) ;
if (!*p || *p == '#')
continue;
if (sscanf(p, "[%[^]]]", token) == 1) {
setid = lookupsetname(token, TRUE);
continue;
}
n = sscanf(p, "%d: %04X%08lX: %[^\n\r]",
&levelnum, &size, &hashval, token);
if (n > 0 && levelnum > 0 && levelnum < 65536 && setid) {
if (n == 1) {
n = sscanf(p, "%*d: %s", token);
if (n > 0 && !strcmp(token, "ok")) {
removefromunslist(setid, levelnum);
continue;
}
} else if (n >= 3) {
addtounslist(setid, levelnum, size, hashval,
n == 4 ? storestring(token) : 0);
continue;
}
}
warn("%s:%d: syntax error", file->name, lineno);
}
return TRUE;
}
/*
* Exported functions.
*/
/* Return TRUE if the given level in the list of unsolvable levels. No
* set name is supplied, so this function relies on the other three
* data. A copy of the level's annotation is made if note is not NULL.
*/
int islevelunsolvable(gamesetup const *game, char *note)
{
int i;
for (i = 0 ; i < listcount ; ++i) {
if (unslist[i].levelnum == game->number
&& unslist[i].size == game->levelsize
&& unslist[i].hashval == game->levelhash) {
if (note)
strcpy(note, getstring(unslist[i].note));
return TRUE;
}
}
return FALSE;
}
/* Look up the levels that constitute the given series and find which
* levels appear in the list. Those that do will have the unsolvable
* field in the gamesetup structure initialized.
*/
int markunsolvablelevels(gameseries *series)
{
int count = 0;
int setid, i, j;
for (j = 0 ; j < series->count ; ++j)
series->games[j].unsolvable = NULL;
setid = lookupsetname(series->name, FALSE);
if (!setid)
return 0;
for (i = 0 ; i < listcount ; ++i) {
if (unslist[i].setid != setid)
continue;
for (j = 0 ; j < series->count ; ++j) {
if (series->games[j].number == unslist[i].levelnum
&& series->games[j].levelsize == unslist[i].size
&& series->games[j].levelhash == unslist[i].hashval) {
series->games[j].unsolvable = getstring(unslist[i].note);
++count;
break;
}
}
}
return count;
}
/* Read the list of unsolvable levels from the given filename. If the
* filename does not contain a path, then the function looks for the
* file in the resource directory and the user's save directory.
*/
int loadunslistfromfile(char const *filename)
{
fileinfo file;
memset(&file, 0, sizeof file);
if (openfileindir(&file, resdir, filename, "r", NULL)) {
readunslist(&file);
fileclose(&file, NULL);
}
if (!haspathname(filename)) {
memset(&file, 0, sizeof file);
if (openfileindir(&file, savedir, filename, "r", NULL)) {
readunslist(&file);
fileclose(&file, NULL);
}
}
return TRUE;
}
/* Free all memory associated with the list of unsolvable levels.
*/
void clearunslist(void)
{
free(unslist);
listcount = 0;
listallocated = 0;
unslist = NULL;
free(names);
namescount = 0;
namesallocated = 0;
names = NULL;
free(strings);
stringsused = 0;
stringsallocated = 0;
strings = NULL;
}