eobish

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