eobish

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