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