eobish

annotate src/tileset.c @ 8:c0e8bbf96849

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