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