rev |
line source |
nuclear@4
|
1 #include <stdio.h>
|
nuclear@4
|
2 #include <stdlib.h>
|
nuclear@4
|
3 #include <string.h>
|
nuclear@5
|
4 #include <stdarg.h>
|
nuclear@4
|
5 #include <errno.h>
|
nuclear@4
|
6 #include <ctype.h>
|
nuclear@4
|
7 #include <assert.h>
|
nuclear@4
|
8 #include "tileset.h"
|
nuclear@4
|
9 #include "image.h"
|
nuclear@4
|
10
|
nuclear@4
|
11 struct orig_node {
|
nuclear@4
|
12 char *prefix;
|
nuclear@4
|
13 struct { int x, y; } *orig;
|
nuclear@4
|
14 int count;
|
nuclear@4
|
15 struct orig_node *next;
|
nuclear@4
|
16 };
|
nuclear@4
|
17
|
nuclear@4
|
18 struct tile_node {
|
nuclear@4
|
19 struct tile tile;
|
nuclear@4
|
20 struct tile_node *next;
|
nuclear@4
|
21 };
|
nuclear@4
|
22
|
nuclear@4
|
23 enum { OPT_IMGDIR, OPT_MAXDIST, OPT_ORIGINS, OPT_PALETTE };
|
nuclear@4
|
24 static struct option {
|
nuclear@4
|
25 int id;
|
nuclear@4
|
26 char *name, *val;
|
nuclear@4
|
27 int ival;
|
nuclear@4
|
28 } opt[] = {
|
nuclear@4
|
29 /* maintain the same order as with the enum above */
|
nuclear@4
|
30 { OPT_IMGDIR, "imagedir", 0, 0 },
|
nuclear@4
|
31 { OPT_MAXDIST, "maxdist", 0, 0 },
|
nuclear@4
|
32 { OPT_ORIGINS, "origins", 0, 0 },
|
nuclear@4
|
33 { OPT_PALETTE, "palette", 0, 0 },
|
nuclear@4
|
34 { 0, 0, 0 }
|
nuclear@4
|
35 };
|
nuclear@4
|
36
|
nuclear@4
|
37 static struct orig_node *load_origin_list(const char *fname);
|
nuclear@4
|
38 static struct orig_node *find_origin(struct orig_node *list, const char *name);
|
nuclear@4
|
39 static char *clean_line(char *buf);
|
nuclear@4
|
40 static struct option *get_option(const char *name);
|
nuclear@4
|
41 static int tilecmp(const void *a, const void *b);
|
nuclear@4
|
42
|
nuclear@4
|
43 int load_tileset(struct tileset *ts, const char *fname)
|
nuclear@4
|
44 {
|
nuclear@4
|
45 FILE *fp;
|
nuclear@4
|
46 char buf[256];
|
nuclear@4
|
47 int i, nline = 0, ntiles = 0;
|
nuclear@4
|
48 struct orig_node *origlist = 0;
|
nuclear@4
|
49 struct tile_node *tlist = 0;
|
nuclear@4
|
50 char *slash, *dir, *imgdir = "";
|
nuclear@4
|
51
|
nuclear@4
|
52 dir = alloca(strlen(fname) + 1);
|
nuclear@4
|
53 strcpy(dir, fname);
|
nuclear@4
|
54 if((slash = strrchr(dir, '/'))) {
|
nuclear@4
|
55 slash[1] = 0;
|
nuclear@4
|
56 } else {
|
nuclear@4
|
57 *dir = 0;
|
nuclear@4
|
58 }
|
nuclear@4
|
59
|
nuclear@4
|
60 if(!(fp = fopen(fname, "r"))) {
|
nuclear@4
|
61 fprintf(stderr, "failed to load tileset: %s: %s\n", fname, strerror(errno));
|
nuclear@4
|
62 return -1;
|
nuclear@4
|
63 }
|
nuclear@4
|
64
|
nuclear@4
|
65 while(fgets(buf, sizeof buf, fp)) {
|
nuclear@4
|
66 char *line, *name, *valstr;
|
nuclear@4
|
67 struct option *opt;
|
nuclear@4
|
68
|
nuclear@4
|
69 ++nline;
|
nuclear@4
|
70
|
nuclear@4
|
71 line = clean_line(buf);
|
nuclear@4
|
72 if(!*line) continue;
|
nuclear@4
|
73
|
nuclear@4
|
74 name = line;
|
nuclear@4
|
75 if(!(valstr = strchr(line, '='))) {
|
nuclear@4
|
76 fprintf(stderr, "tileset: %s line %d: invalid syntax: %s\n", fname, nline, line);
|
nuclear@4
|
77 goto fail;
|
nuclear@4
|
78 }
|
nuclear@4
|
79 *valstr++ = 0;
|
nuclear@4
|
80
|
nuclear@4
|
81 name = clean_line(name);
|
nuclear@4
|
82 valstr = clean_line(valstr);
|
nuclear@4
|
83
|
nuclear@4
|
84 if(!*name || !*valstr) {
|
nuclear@4
|
85 fprintf(stderr, "tileset: %s line %d: malformed line\n", fname, nline);
|
nuclear@4
|
86 goto fail;
|
nuclear@4
|
87 }
|
nuclear@4
|
88
|
nuclear@4
|
89 /* first check to see if it's an option */
|
nuclear@4
|
90 if((opt = get_option(name))) {
|
nuclear@4
|
91 opt->val = strdup(valstr);
|
nuclear@4
|
92 opt->ival = atoi(valstr);
|
nuclear@4
|
93
|
nuclear@4
|
94 switch(opt->id) {
|
nuclear@4
|
95 case OPT_IMGDIR:
|
nuclear@4
|
96 {
|
nuclear@4
|
97 int len = strlen(opt->val);
|
nuclear@4
|
98 imgdir = alloca(len + 2);
|
nuclear@4
|
99 strcpy(imgdir, opt->val);
|
nuclear@4
|
100
|
nuclear@4
|
101 if(imgdir[len - 1] != '/') {
|
nuclear@4
|
102 imgdir[len] = '/';
|
nuclear@4
|
103 imgdir[len + 1] = 0;
|
nuclear@4
|
104 }
|
nuclear@4
|
105 printf("imgdir: \"%s\"\n", imgdir);
|
nuclear@4
|
106 }
|
nuclear@4
|
107 break;
|
nuclear@4
|
108
|
nuclear@4
|
109 case OPT_ORIGINS:
|
nuclear@4
|
110 {
|
nuclear@4
|
111 char *path = alloca(strlen(dir) + strlen(opt->val) + 1);
|
nuclear@4
|
112 sprintf(path, "%s%s", dir, opt->val);
|
nuclear@4
|
113
|
nuclear@4
|
114 if(!(origlist = load_origin_list(path))) {
|
nuclear@4
|
115 goto failmsg;
|
nuclear@4
|
116 }
|
nuclear@4
|
117 }
|
nuclear@4
|
118 default:
|
nuclear@4
|
119 break;
|
nuclear@4
|
120 }
|
nuclear@4
|
121
|
nuclear@4
|
122 } else {
|
nuclear@4
|
123 /* otherwise it's just a tile... add it to the list */
|
nuclear@4
|
124 char *path;
|
nuclear@4
|
125 struct tile_node *tnode;
|
nuclear@4
|
126
|
nuclear@4
|
127 if(!(tnode = malloc(sizeof *tnode))) {
|
nuclear@4
|
128 goto failmsg;
|
nuclear@4
|
129 }
|
nuclear@4
|
130 if(!(tnode->tile.name = strdup(name))) {
|
nuclear@4
|
131 free(tnode);
|
nuclear@4
|
132 goto failmsg;
|
nuclear@4
|
133 }
|
nuclear@4
|
134
|
nuclear@4
|
135 if(!(path = malloc(strlen(dir) + strlen(imgdir) + strlen(valstr) + 1))) {
|
nuclear@4
|
136 free(tnode->tile.name);
|
nuclear@4
|
137 free(tnode);
|
nuclear@4
|
138 goto failmsg;
|
nuclear@4
|
139 }
|
nuclear@4
|
140
|
nuclear@4
|
141 sprintf(path, "%s%s%s", dir, imgdir, valstr);
|
nuclear@4
|
142 if(load_image(&tnode->tile.img, path) == -1) {
|
nuclear@4
|
143 free(tnode->tile.name);
|
nuclear@4
|
144 free(tnode);
|
nuclear@4
|
145 free(path);
|
nuclear@4
|
146 goto failmsg;
|
nuclear@4
|
147 }
|
nuclear@4
|
148 free(path);
|
nuclear@4
|
149 tnode->next = tlist;
|
nuclear@4
|
150 tlist = tnode;
|
nuclear@4
|
151 ++ntiles;
|
nuclear@4
|
152 }
|
nuclear@4
|
153 }
|
nuclear@4
|
154 fclose(fp), fp = 0;
|
nuclear@4
|
155
|
nuclear@4
|
156 /* if there is a palette, load it */
|
nuclear@4
|
157 if(opt[OPT_PALETTE].val) {
|
nuclear@4
|
158 const char *fname = opt[OPT_PALETTE].val;
|
nuclear@4
|
159 char *path = alloca(strlen(dir) + strlen(fname) + 1);
|
nuclear@4
|
160 sprintf(path, "%s%s", dir, fname);
|
nuclear@4
|
161
|
nuclear@4
|
162 if((ts->pal_size = load_palette(ts->pal, path)) == -1) {
|
nuclear@4
|
163 goto failmsg;
|
nuclear@4
|
164 }
|
nuclear@4
|
165 }
|
nuclear@4
|
166
|
nuclear@4
|
167 /* allocate the array of tiles and copy all the tiles from the list */
|
nuclear@4
|
168 if(!(ts->tile = calloc(ntiles, sizeof *ts->tile))) {
|
nuclear@4
|
169 goto failmsg;
|
nuclear@4
|
170 }
|
nuclear@4
|
171 ts->num_tiles = ntiles;
|
nuclear@4
|
172
|
nuclear@4
|
173 i = 0;
|
nuclear@4
|
174 while(tlist) {
|
nuclear@4
|
175 int idx;
|
nuclear@4
|
176 struct orig_node *onode;
|
nuclear@4
|
177 struct tile_node *tnode = tlist;
|
nuclear@4
|
178 tlist = tlist->next;
|
nuclear@4
|
179
|
nuclear@4
|
180 ts->tile[i] = tnode->tile;
|
nuclear@4
|
181 free(tnode);
|
nuclear@4
|
182
|
nuclear@4
|
183 /* determine the origin index by the tile z number,
|
nuclear@4
|
184 * which if it exists, it starts at name[3]
|
nuclear@4
|
185 */
|
nuclear@4
|
186 idx = atoi(ts->tile[i].name + 3);
|
nuclear@4
|
187
|
nuclear@4
|
188 /* retrieve the origin for this tile */
|
nuclear@4
|
189 if((onode = find_origin(origlist, ts->tile[i].name))) {
|
nuclear@4
|
190 if(idx >= onode->count) idx = onode->count - 1;
|
nuclear@4
|
191 ts->tile[i].orig_x = onode->orig[idx].x;
|
nuclear@4
|
192 ts->tile[i].orig_y = onode->orig[idx].y;
|
nuclear@4
|
193 }
|
nuclear@4
|
194 ++i;
|
nuclear@4
|
195 }
|
nuclear@4
|
196
|
nuclear@4
|
197 /* now we need to sort them so that we can binary-search tiles later in real-time */
|
nuclear@4
|
198 qsort(ts->tile, ntiles, sizeof *ts->tile, tilecmp);
|
nuclear@4
|
199
|
nuclear@4
|
200 printf("loaded tileset %s\n", fname);
|
nuclear@4
|
201 for(i=0; i<ntiles; i++) {
|
nuclear@5
|
202 printf(" tile: %s (orig: %d,%d)\n", ts->tile[i].name, ts->tile[i].orig_x, ts->tile[i].orig_y);
|
nuclear@4
|
203 }
|
nuclear@4
|
204
|
nuclear@4
|
205 return 0;
|
nuclear@4
|
206
|
nuclear@4
|
207 failmsg:
|
nuclear@4
|
208 fprintf(stderr, "failed to load tileset: %s\n", fname);
|
nuclear@4
|
209 fail:
|
nuclear@4
|
210 if(fp) fclose(fp);
|
nuclear@4
|
211 while(tlist) {
|
nuclear@4
|
212 struct tile_node *n = tlist;
|
nuclear@4
|
213 tlist = tlist->next;
|
nuclear@4
|
214 free(n->tile.name);
|
nuclear@4
|
215 destroy_image(&n->tile.img);
|
nuclear@4
|
216 free(n);
|
nuclear@4
|
217 }
|
nuclear@4
|
218 return -1;
|
nuclear@4
|
219 }
|
nuclear@4
|
220
|
nuclear@4
|
221 void destroy_tileset(struct tileset *ts)
|
nuclear@4
|
222 {
|
nuclear@4
|
223 int i;
|
nuclear@4
|
224
|
nuclear@4
|
225 if(!ts) return;
|
nuclear@4
|
226
|
nuclear@4
|
227 if(ts->tile) {
|
nuclear@4
|
228 for(i=0; i<ts->num_tiles; i++) {
|
nuclear@4
|
229 free(ts->tile[i].name);
|
nuclear@4
|
230 destroy_image(&ts->tile[i].img);
|
nuclear@4
|
231 }
|
nuclear@4
|
232 free(ts->tile);
|
nuclear@4
|
233 ts->tile = 0;
|
nuclear@4
|
234 }
|
nuclear@4
|
235 }
|
nuclear@4
|
236
|
nuclear@4
|
237 struct tile *get_tile(struct tileset *ts, const char *name)
|
nuclear@4
|
238 {
|
nuclear@4
|
239 struct tile key;
|
nuclear@4
|
240 key.name = (char*)name;
|
nuclear@4
|
241 return bsearch(&key, ts->tile, ts->num_tiles, sizeof *ts->tile, tilecmp);
|
nuclear@4
|
242 }
|
nuclear@4
|
243
|
nuclear@5
|
244 struct tile *get_tilef(struct tileset *ts, const char *fmt, ...)
|
nuclear@5
|
245 {
|
nuclear@5
|
246 va_list ap;
|
nuclear@5
|
247 int sz = strlen(fmt) * 2;
|
nuclear@5
|
248 char *buf = alloca(sz + 1);
|
nuclear@5
|
249
|
nuclear@5
|
250 va_start(ap, fmt);
|
nuclear@5
|
251 vsnprintf(buf, sz, fmt, ap);
|
nuclear@5
|
252 va_end(ap);
|
nuclear@5
|
253
|
nuclear@5
|
254 return get_tile(ts, buf);
|
nuclear@5
|
255 }
|
nuclear@5
|
256
|
nuclear@4
|
257 static struct orig_node *load_origin_list(const char *fname)
|
nuclear@4
|
258 {
|
nuclear@4
|
259 FILE *fp;
|
nuclear@4
|
260 char buf[256];
|
nuclear@4
|
261 int i, valbuf[2][64], num, nline = 0;
|
nuclear@4
|
262 struct orig_node *head = 0;
|
nuclear@4
|
263
|
nuclear@4
|
264 if(!(fp = fopen(fname, "r"))) {
|
nuclear@4
|
265 fprintf(stderr, "failed to load origins file: %s: %s\n", fname, strerror(errno));
|
nuclear@4
|
266 return 0;
|
nuclear@4
|
267 }
|
nuclear@4
|
268
|
nuclear@4
|
269 while(fgets(buf, sizeof buf, fp)) {
|
nuclear@4
|
270 char *tok, *name, *line;
|
nuclear@4
|
271 struct orig_node *onode;
|
nuclear@4
|
272
|
nuclear@4
|
273 ++nline;
|
nuclear@4
|
274
|
nuclear@4
|
275 line = clean_line(buf);
|
nuclear@4
|
276 if(!*line) continue;
|
nuclear@4
|
277
|
nuclear@4
|
278 if(!(name = strtok(line, " \t\n\r"))) {
|
nuclear@4
|
279 continue;
|
nuclear@4
|
280 }
|
nuclear@4
|
281 num = 0;
|
nuclear@4
|
282 while(num < 64 && (tok = strtok(0, " \t\n\r"))) {
|
nuclear@4
|
283 if(sscanf(tok, "%dx%d", valbuf[0] + num, valbuf[1] + num) != 2) {
|
nuclear@4
|
284 fprintf(stderr, "failed to load origins file: %s: ", fname);
|
nuclear@4
|
285 fprintf(stderr, "invalid token \"%s\" on line %d (%s)\n", tok, nline, name);
|
nuclear@4
|
286 goto fail;
|
nuclear@4
|
287 }
|
nuclear@4
|
288 ++num;
|
nuclear@4
|
289 }
|
nuclear@4
|
290 if(num >= 64) {
|
nuclear@4
|
291 fprintf(stderr, "warning: origins file %s: excessive number of %s origins on line %d\n", fname, name, nline);
|
nuclear@4
|
292 }
|
nuclear@4
|
293
|
nuclear@4
|
294 if(!(onode = malloc(sizeof *onode))) {
|
nuclear@4
|
295 fprintf(stderr, "failed to allocate origins node\n");
|
nuclear@4
|
296 goto fail;
|
nuclear@4
|
297 }
|
nuclear@4
|
298 if(!(onode->prefix = malloc(strlen(name) + 1))) {
|
nuclear@4
|
299 fprintf(stderr, "failed to allocate origins name\n");
|
nuclear@4
|
300 free(onode);
|
nuclear@4
|
301 goto fail;
|
nuclear@4
|
302 }
|
nuclear@4
|
303 if(!(onode->orig = malloc(num * sizeof *onode->orig))) {
|
nuclear@4
|
304 fprintf(stderr, "failed to allocate origins array\n");
|
nuclear@4
|
305 free(onode);
|
nuclear@4
|
306 goto fail;
|
nuclear@4
|
307 }
|
nuclear@4
|
308
|
nuclear@4
|
309 strcpy(onode->prefix, name);
|
nuclear@4
|
310 onode->count = num;
|
nuclear@4
|
311 onode->next = head;
|
nuclear@4
|
312 head = onode;
|
nuclear@4
|
313
|
nuclear@4
|
314 for(i=0; i<num; i++) {
|
nuclear@4
|
315 onode->orig[i].x = valbuf[0][i];
|
nuclear@4
|
316 onode->orig[i].y = valbuf[1][i];
|
nuclear@4
|
317 }
|
nuclear@4
|
318 }
|
nuclear@4
|
319 fclose(fp);
|
nuclear@4
|
320 return head;
|
nuclear@4
|
321
|
nuclear@4
|
322 fail:
|
nuclear@4
|
323 fclose(fp);
|
nuclear@4
|
324 while(head) {
|
nuclear@4
|
325 struct orig_node *tmp = head;
|
nuclear@4
|
326 head = head->next;
|
nuclear@4
|
327 free(tmp->prefix);
|
nuclear@4
|
328 free(tmp->orig);
|
nuclear@4
|
329 }
|
nuclear@4
|
330 return 0;
|
nuclear@4
|
331 }
|
nuclear@4
|
332
|
nuclear@4
|
333 static struct orig_node *find_origin(struct orig_node *list, const char *name)
|
nuclear@4
|
334 {
|
nuclear@4
|
335 struct orig_node *node = list;
|
nuclear@4
|
336 struct orig_node *best = 0;
|
nuclear@4
|
337 int best_len = 0;
|
nuclear@4
|
338
|
nuclear@4
|
339 while(node) {
|
nuclear@4
|
340 const char *nmptr = name;
|
nuclear@4
|
341 const char *pptr = node->prefix;
|
nuclear@4
|
342 int len;
|
nuclear@4
|
343
|
nuclear@4
|
344 while(*pptr && *nmptr && *pptr == *nmptr) ++pptr, ++nmptr;
|
nuclear@4
|
345 if((len = nmptr - name) > best_len) {
|
nuclear@4
|
346 best = node;
|
nuclear@4
|
347 best_len = len;
|
nuclear@4
|
348 }
|
nuclear@4
|
349 node = node->next;
|
nuclear@4
|
350 }
|
nuclear@4
|
351 return best;
|
nuclear@4
|
352 }
|
nuclear@4
|
353
|
nuclear@4
|
354 static char *clean_line(char *buf)
|
nuclear@4
|
355 {
|
nuclear@4
|
356 char *end = buf + strlen(buf);
|
nuclear@4
|
357
|
nuclear@4
|
358 while(*buf && isspace(*buf)) ++buf;
|
nuclear@4
|
359 while(end > buf && (!*end || isspace(*end))) {
|
nuclear@4
|
360 *end-- = 0;
|
nuclear@4
|
361 }
|
nuclear@4
|
362
|
nuclear@4
|
363 /* also remove comments */
|
nuclear@4
|
364 if((end = strchr(buf, '#'))) {
|
nuclear@4
|
365 *end = 0;
|
nuclear@4
|
366 }
|
nuclear@4
|
367 return buf;
|
nuclear@4
|
368 }
|
nuclear@4
|
369
|
nuclear@4
|
370 static struct option *get_option(const char *name)
|
nuclear@4
|
371 {
|
nuclear@4
|
372 int i;
|
nuclear@4
|
373 for(i=0; opt[i].name; i++) {
|
nuclear@4
|
374 if(strcmp(name, opt[i].name) == 0) {
|
nuclear@4
|
375 assert(i == opt[i].id);
|
nuclear@4
|
376 return opt + i;
|
nuclear@4
|
377 }
|
nuclear@4
|
378 }
|
nuclear@4
|
379 return 0;
|
nuclear@4
|
380 }
|
nuclear@4
|
381
|
nuclear@4
|
382 /* tile comparison for sorting the array of tiles by name */
|
nuclear@4
|
383 static int tilecmp(const void *a, const void *b)
|
nuclear@4
|
384 {
|
nuclear@4
|
385 const struct tile *ta = a;
|
nuclear@4
|
386 const struct tile *tb = b;
|
nuclear@4
|
387 return strcmp(ta->name, tb->name);
|
nuclear@4
|
388 }
|