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