eobish

view src/tileset.c @ 4:ce0548d24918

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