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