eobish
diff 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 diff
1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/tileset.c Sun Jan 18 13:30:30 2015 +0200 1.3 @@ -0,0 +1,374 @@ 1.4 +#include <stdio.h> 1.5 +#include <stdlib.h> 1.6 +#include <string.h> 1.7 +#include <errno.h> 1.8 +#include <ctype.h> 1.9 +#include <assert.h> 1.10 +#include "tileset.h" 1.11 +#include "image.h" 1.12 + 1.13 +struct orig_node { 1.14 + char *prefix; 1.15 + struct { int x, y; } *orig; 1.16 + int count; 1.17 + struct orig_node *next; 1.18 +}; 1.19 + 1.20 +struct tile_node { 1.21 + struct tile tile; 1.22 + struct tile_node *next; 1.23 +}; 1.24 + 1.25 +enum { OPT_IMGDIR, OPT_MAXDIST, OPT_ORIGINS, OPT_PALETTE }; 1.26 +static struct option { 1.27 + int id; 1.28 + char *name, *val; 1.29 + int ival; 1.30 +} opt[] = { 1.31 + /* maintain the same order as with the enum above */ 1.32 + { OPT_IMGDIR, "imagedir", 0, 0 }, 1.33 + { OPT_MAXDIST, "maxdist", 0, 0 }, 1.34 + { OPT_ORIGINS, "origins", 0, 0 }, 1.35 + { OPT_PALETTE, "palette", 0, 0 }, 1.36 + { 0, 0, 0 } 1.37 +}; 1.38 + 1.39 +static struct orig_node *load_origin_list(const char *fname); 1.40 +static struct orig_node *find_origin(struct orig_node *list, const char *name); 1.41 +static char *clean_line(char *buf); 1.42 +static struct option *get_option(const char *name); 1.43 +static int tilecmp(const void *a, const void *b); 1.44 + 1.45 +int load_tileset(struct tileset *ts, const char *fname) 1.46 +{ 1.47 + FILE *fp; 1.48 + char buf[256]; 1.49 + int i, nline = 0, ntiles = 0; 1.50 + struct orig_node *origlist = 0; 1.51 + struct tile_node *tlist = 0; 1.52 + char *slash, *dir, *imgdir = ""; 1.53 + 1.54 + dir = alloca(strlen(fname) + 1); 1.55 + strcpy(dir, fname); 1.56 + if((slash = strrchr(dir, '/'))) { 1.57 + slash[1] = 0; 1.58 + } else { 1.59 + *dir = 0; 1.60 + } 1.61 + 1.62 + if(!(fp = fopen(fname, "r"))) { 1.63 + fprintf(stderr, "failed to load tileset: %s: %s\n", fname, strerror(errno)); 1.64 + return -1; 1.65 + } 1.66 + 1.67 + while(fgets(buf, sizeof buf, fp)) { 1.68 + char *line, *name, *valstr; 1.69 + struct option *opt; 1.70 + 1.71 + ++nline; 1.72 + 1.73 + line = clean_line(buf); 1.74 + if(!*line) continue; 1.75 + 1.76 + name = line; 1.77 + if(!(valstr = strchr(line, '='))) { 1.78 + fprintf(stderr, "tileset: %s line %d: invalid syntax: %s\n", fname, nline, line); 1.79 + goto fail; 1.80 + } 1.81 + *valstr++ = 0; 1.82 + 1.83 + name = clean_line(name); 1.84 + valstr = clean_line(valstr); 1.85 + 1.86 + if(!*name || !*valstr) { 1.87 + fprintf(stderr, "tileset: %s line %d: malformed line\n", fname, nline); 1.88 + goto fail; 1.89 + } 1.90 + 1.91 + /* first check to see if it's an option */ 1.92 + if((opt = get_option(name))) { 1.93 + opt->val = strdup(valstr); 1.94 + opt->ival = atoi(valstr); 1.95 + 1.96 + switch(opt->id) { 1.97 + case OPT_IMGDIR: 1.98 + { 1.99 + int len = strlen(opt->val); 1.100 + imgdir = alloca(len + 2); 1.101 + strcpy(imgdir, opt->val); 1.102 + 1.103 + if(imgdir[len - 1] != '/') { 1.104 + imgdir[len] = '/'; 1.105 + imgdir[len + 1] = 0; 1.106 + } 1.107 + printf("imgdir: \"%s\"\n", imgdir); 1.108 + } 1.109 + break; 1.110 + 1.111 + case OPT_ORIGINS: 1.112 + { 1.113 + char *path = alloca(strlen(dir) + strlen(opt->val) + 1); 1.114 + sprintf(path, "%s%s", dir, opt->val); 1.115 + 1.116 + if(!(origlist = load_origin_list(path))) { 1.117 + goto failmsg; 1.118 + } 1.119 + } 1.120 + default: 1.121 + break; 1.122 + } 1.123 + 1.124 + } else { 1.125 + /* otherwise it's just a tile... add it to the list */ 1.126 + char *path; 1.127 + struct tile_node *tnode; 1.128 + 1.129 + if(!(tnode = malloc(sizeof *tnode))) { 1.130 + goto failmsg; 1.131 + } 1.132 + if(!(tnode->tile.name = strdup(name))) { 1.133 + free(tnode); 1.134 + goto failmsg; 1.135 + } 1.136 + 1.137 + if(!(path = malloc(strlen(dir) + strlen(imgdir) + strlen(valstr) + 1))) { 1.138 + free(tnode->tile.name); 1.139 + free(tnode); 1.140 + goto failmsg; 1.141 + } 1.142 + 1.143 + sprintf(path, "%s%s%s", dir, imgdir, valstr); 1.144 + if(load_image(&tnode->tile.img, path) == -1) { 1.145 + free(tnode->tile.name); 1.146 + free(tnode); 1.147 + free(path); 1.148 + goto failmsg; 1.149 + } 1.150 + free(path); 1.151 + tnode->next = tlist; 1.152 + tlist = tnode; 1.153 + ++ntiles; 1.154 + } 1.155 + } 1.156 + fclose(fp), fp = 0; 1.157 + 1.158 + /* if there is a palette, load it */ 1.159 + if(opt[OPT_PALETTE].val) { 1.160 + const char *fname = opt[OPT_PALETTE].val; 1.161 + char *path = alloca(strlen(dir) + strlen(fname) + 1); 1.162 + sprintf(path, "%s%s", dir, fname); 1.163 + 1.164 + if((ts->pal_size = load_palette(ts->pal, path)) == -1) { 1.165 + goto failmsg; 1.166 + } 1.167 + } 1.168 + 1.169 + /* allocate the array of tiles and copy all the tiles from the list */ 1.170 + if(!(ts->tile = calloc(ntiles, sizeof *ts->tile))) { 1.171 + goto failmsg; 1.172 + } 1.173 + ts->num_tiles = ntiles; 1.174 + 1.175 + i = 0; 1.176 + while(tlist) { 1.177 + int idx; 1.178 + struct orig_node *onode; 1.179 + struct tile_node *tnode = tlist; 1.180 + tlist = tlist->next; 1.181 + 1.182 + ts->tile[i] = tnode->tile; 1.183 + free(tnode); 1.184 + 1.185 + /* determine the origin index by the tile z number, 1.186 + * which if it exists, it starts at name[3] 1.187 + */ 1.188 + idx = atoi(ts->tile[i].name + 3); 1.189 + 1.190 + /* retrieve the origin for this tile */ 1.191 + if((onode = find_origin(origlist, ts->tile[i].name))) { 1.192 + if(idx >= onode->count) idx = onode->count - 1; 1.193 + ts->tile[i].orig_x = onode->orig[idx].x; 1.194 + ts->tile[i].orig_y = onode->orig[idx].y; 1.195 + } 1.196 + ++i; 1.197 + } 1.198 + 1.199 + /* now we need to sort them so that we can binary-search tiles later in real-time */ 1.200 + qsort(ts->tile, ntiles, sizeof *ts->tile, tilecmp); 1.201 + 1.202 + printf("loaded tileset %s\n", fname); 1.203 + for(i=0; i<ntiles; i++) { 1.204 + printf(" tile: %s\n", ts->tile[i].name); 1.205 + } 1.206 + 1.207 + return 0; 1.208 + 1.209 +failmsg: 1.210 + fprintf(stderr, "failed to load tileset: %s\n", fname); 1.211 +fail: 1.212 + if(fp) fclose(fp); 1.213 + while(tlist) { 1.214 + struct tile_node *n = tlist; 1.215 + tlist = tlist->next; 1.216 + free(n->tile.name); 1.217 + destroy_image(&n->tile.img); 1.218 + free(n); 1.219 + } 1.220 + return -1; 1.221 +} 1.222 + 1.223 +void destroy_tileset(struct tileset *ts) 1.224 +{ 1.225 + int i; 1.226 + 1.227 + if(!ts) return; 1.228 + 1.229 + if(ts->tile) { 1.230 + for(i=0; i<ts->num_tiles; i++) { 1.231 + free(ts->tile[i].name); 1.232 + destroy_image(&ts->tile[i].img); 1.233 + } 1.234 + free(ts->tile); 1.235 + ts->tile = 0; 1.236 + } 1.237 +} 1.238 + 1.239 +struct tile *get_tile(struct tileset *ts, const char *name) 1.240 +{ 1.241 + struct tile key; 1.242 + key.name = (char*)name; 1.243 + return bsearch(&key, ts->tile, ts->num_tiles, sizeof *ts->tile, tilecmp); 1.244 +} 1.245 + 1.246 +static struct orig_node *load_origin_list(const char *fname) 1.247 +{ 1.248 + FILE *fp; 1.249 + char buf[256]; 1.250 + int i, valbuf[2][64], num, nline = 0; 1.251 + struct orig_node *head = 0; 1.252 + 1.253 + if(!(fp = fopen(fname, "r"))) { 1.254 + fprintf(stderr, "failed to load origins file: %s: %s\n", fname, strerror(errno)); 1.255 + return 0; 1.256 + } 1.257 + 1.258 + while(fgets(buf, sizeof buf, fp)) { 1.259 + char *tok, *name, *line; 1.260 + struct orig_node *onode; 1.261 + 1.262 + ++nline; 1.263 + 1.264 + line = clean_line(buf); 1.265 + if(!*line) continue; 1.266 + 1.267 + if(!(name = strtok(line, " \t\n\r"))) { 1.268 + continue; 1.269 + } 1.270 + num = 0; 1.271 + while(num < 64 && (tok = strtok(0, " \t\n\r"))) { 1.272 + if(sscanf(tok, "%dx%d", valbuf[0] + num, valbuf[1] + num) != 2) { 1.273 + fprintf(stderr, "failed to load origins file: %s: ", fname); 1.274 + fprintf(stderr, "invalid token \"%s\" on line %d (%s)\n", tok, nline, name); 1.275 + goto fail; 1.276 + } 1.277 + ++num; 1.278 + } 1.279 + if(num >= 64) { 1.280 + fprintf(stderr, "warning: origins file %s: excessive number of %s origins on line %d\n", fname, name, nline); 1.281 + } 1.282 + 1.283 + if(!(onode = malloc(sizeof *onode))) { 1.284 + fprintf(stderr, "failed to allocate origins node\n"); 1.285 + goto fail; 1.286 + } 1.287 + if(!(onode->prefix = malloc(strlen(name) + 1))) { 1.288 + fprintf(stderr, "failed to allocate origins name\n"); 1.289 + free(onode); 1.290 + goto fail; 1.291 + } 1.292 + if(!(onode->orig = malloc(num * sizeof *onode->orig))) { 1.293 + fprintf(stderr, "failed to allocate origins array\n"); 1.294 + free(onode); 1.295 + goto fail; 1.296 + } 1.297 + 1.298 + strcpy(onode->prefix, name); 1.299 + onode->count = num; 1.300 + onode->next = head; 1.301 + head = onode; 1.302 + 1.303 + for(i=0; i<num; i++) { 1.304 + onode->orig[i].x = valbuf[0][i]; 1.305 + onode->orig[i].y = valbuf[1][i]; 1.306 + } 1.307 + } 1.308 + fclose(fp); 1.309 + return head; 1.310 + 1.311 +fail: 1.312 + fclose(fp); 1.313 + while(head) { 1.314 + struct orig_node *tmp = head; 1.315 + head = head->next; 1.316 + free(tmp->prefix); 1.317 + free(tmp->orig); 1.318 + } 1.319 + return 0; 1.320 +} 1.321 + 1.322 +static struct orig_node *find_origin(struct orig_node *list, const char *name) 1.323 +{ 1.324 + struct orig_node *node = list; 1.325 + struct orig_node *best = 0; 1.326 + int best_len = 0; 1.327 + 1.328 + while(node) { 1.329 + const char *nmptr = name; 1.330 + const char *pptr = node->prefix; 1.331 + int len; 1.332 + 1.333 + while(*pptr && *nmptr && *pptr == *nmptr) ++pptr, ++nmptr; 1.334 + if((len = nmptr - name) > best_len) { 1.335 + best = node; 1.336 + best_len = len; 1.337 + } 1.338 + node = node->next; 1.339 + } 1.340 + return best; 1.341 +} 1.342 + 1.343 +static char *clean_line(char *buf) 1.344 +{ 1.345 + char *end = buf + strlen(buf); 1.346 + 1.347 + while(*buf && isspace(*buf)) ++buf; 1.348 + while(end > buf && (!*end || isspace(*end))) { 1.349 + *end-- = 0; 1.350 + } 1.351 + 1.352 + /* also remove comments */ 1.353 + if((end = strchr(buf, '#'))) { 1.354 + *end = 0; 1.355 + } 1.356 + return buf; 1.357 +} 1.358 + 1.359 +static struct option *get_option(const char *name) 1.360 +{ 1.361 + int i; 1.362 + for(i=0; opt[i].name; i++) { 1.363 + if(strcmp(name, opt[i].name) == 0) { 1.364 + assert(i == opt[i].id); 1.365 + return opt + i; 1.366 + } 1.367 + } 1.368 + return 0; 1.369 +} 1.370 + 1.371 +/* tile comparison for sorting the array of tiles by name */ 1.372 +static int tilecmp(const void *a, const void *b) 1.373 +{ 1.374 + const struct tile *ta = a; 1.375 + const struct tile *tb = b; 1.376 + return strcmp(ta->name, tb->name); 1.377 +}