nuclear@4: #include nuclear@4: #include nuclear@4: #include nuclear@4: #include nuclear@4: #include nuclear@4: #include nuclear@4: #include "tileset.h" nuclear@4: #include "image.h" nuclear@4: nuclear@4: struct orig_node { nuclear@4: char *prefix; nuclear@4: struct { int x, y; } *orig; nuclear@4: int count; nuclear@4: struct orig_node *next; nuclear@4: }; nuclear@4: nuclear@4: struct tile_node { nuclear@4: struct tile tile; nuclear@4: struct tile_node *next; nuclear@4: }; nuclear@4: nuclear@4: enum { OPT_IMGDIR, OPT_MAXDIST, OPT_ORIGINS, OPT_PALETTE }; nuclear@4: static struct option { nuclear@4: int id; nuclear@4: char *name, *val; nuclear@4: int ival; nuclear@4: } opt[] = { nuclear@4: /* maintain the same order as with the enum above */ nuclear@4: { OPT_IMGDIR, "imagedir", 0, 0 }, nuclear@4: { OPT_MAXDIST, "maxdist", 0, 0 }, nuclear@4: { OPT_ORIGINS, "origins", 0, 0 }, nuclear@4: { OPT_PALETTE, "palette", 0, 0 }, nuclear@4: { 0, 0, 0 } nuclear@4: }; nuclear@4: nuclear@4: static struct orig_node *load_origin_list(const char *fname); nuclear@4: static struct orig_node *find_origin(struct orig_node *list, const char *name); nuclear@4: static char *clean_line(char *buf); nuclear@4: static struct option *get_option(const char *name); nuclear@4: static int tilecmp(const void *a, const void *b); nuclear@4: nuclear@4: int load_tileset(struct tileset *ts, const char *fname) nuclear@4: { nuclear@4: FILE *fp; nuclear@4: char buf[256]; nuclear@4: int i, nline = 0, ntiles = 0; nuclear@4: struct orig_node *origlist = 0; nuclear@4: struct tile_node *tlist = 0; nuclear@4: char *slash, *dir, *imgdir = ""; nuclear@4: nuclear@4: dir = alloca(strlen(fname) + 1); nuclear@4: strcpy(dir, fname); nuclear@4: if((slash = strrchr(dir, '/'))) { nuclear@4: slash[1] = 0; nuclear@4: } else { nuclear@4: *dir = 0; nuclear@4: } nuclear@4: nuclear@4: if(!(fp = fopen(fname, "r"))) { nuclear@4: fprintf(stderr, "failed to load tileset: %s: %s\n", fname, strerror(errno)); nuclear@4: return -1; nuclear@4: } nuclear@4: nuclear@4: while(fgets(buf, sizeof buf, fp)) { nuclear@4: char *line, *name, *valstr; nuclear@4: struct option *opt; nuclear@4: nuclear@4: ++nline; nuclear@4: nuclear@4: line = clean_line(buf); nuclear@4: if(!*line) continue; nuclear@4: nuclear@4: name = line; nuclear@4: if(!(valstr = strchr(line, '='))) { nuclear@4: fprintf(stderr, "tileset: %s line %d: invalid syntax: %s\n", fname, nline, line); nuclear@4: goto fail; nuclear@4: } nuclear@4: *valstr++ = 0; nuclear@4: nuclear@4: name = clean_line(name); nuclear@4: valstr = clean_line(valstr); nuclear@4: nuclear@4: if(!*name || !*valstr) { nuclear@4: fprintf(stderr, "tileset: %s line %d: malformed line\n", fname, nline); nuclear@4: goto fail; nuclear@4: } nuclear@4: nuclear@4: /* first check to see if it's an option */ nuclear@4: if((opt = get_option(name))) { nuclear@4: opt->val = strdup(valstr); nuclear@4: opt->ival = atoi(valstr); nuclear@4: nuclear@4: switch(opt->id) { nuclear@4: case OPT_IMGDIR: nuclear@4: { nuclear@4: int len = strlen(opt->val); nuclear@4: imgdir = alloca(len + 2); nuclear@4: strcpy(imgdir, opt->val); nuclear@4: nuclear@4: if(imgdir[len - 1] != '/') { nuclear@4: imgdir[len] = '/'; nuclear@4: imgdir[len + 1] = 0; nuclear@4: } nuclear@4: printf("imgdir: \"%s\"\n", imgdir); nuclear@4: } nuclear@4: break; nuclear@4: nuclear@4: case OPT_ORIGINS: nuclear@4: { nuclear@4: char *path = alloca(strlen(dir) + strlen(opt->val) + 1); nuclear@4: sprintf(path, "%s%s", dir, opt->val); nuclear@4: nuclear@4: if(!(origlist = load_origin_list(path))) { nuclear@4: goto failmsg; nuclear@4: } nuclear@4: } nuclear@4: default: nuclear@4: break; nuclear@4: } nuclear@4: nuclear@4: } else { nuclear@4: /* otherwise it's just a tile... add it to the list */ nuclear@4: char *path; nuclear@4: struct tile_node *tnode; nuclear@4: nuclear@4: if(!(tnode = malloc(sizeof *tnode))) { nuclear@4: goto failmsg; nuclear@4: } nuclear@4: if(!(tnode->tile.name = strdup(name))) { nuclear@4: free(tnode); nuclear@4: goto failmsg; nuclear@4: } nuclear@4: nuclear@4: if(!(path = malloc(strlen(dir) + strlen(imgdir) + strlen(valstr) + 1))) { nuclear@4: free(tnode->tile.name); nuclear@4: free(tnode); nuclear@4: goto failmsg; nuclear@4: } nuclear@4: nuclear@4: sprintf(path, "%s%s%s", dir, imgdir, valstr); nuclear@4: if(load_image(&tnode->tile.img, path) == -1) { nuclear@4: free(tnode->tile.name); nuclear@4: free(tnode); nuclear@4: free(path); nuclear@4: goto failmsg; nuclear@4: } nuclear@4: free(path); nuclear@4: tnode->next = tlist; nuclear@4: tlist = tnode; nuclear@4: ++ntiles; nuclear@4: } nuclear@4: } nuclear@4: fclose(fp), fp = 0; nuclear@4: nuclear@4: /* if there is a palette, load it */ nuclear@4: if(opt[OPT_PALETTE].val) { nuclear@4: const char *fname = opt[OPT_PALETTE].val; nuclear@4: char *path = alloca(strlen(dir) + strlen(fname) + 1); nuclear@4: sprintf(path, "%s%s", dir, fname); nuclear@4: nuclear@4: if((ts->pal_size = load_palette(ts->pal, path)) == -1) { nuclear@4: goto failmsg; nuclear@4: } nuclear@4: } nuclear@4: nuclear@4: /* allocate the array of tiles and copy all the tiles from the list */ nuclear@4: if(!(ts->tile = calloc(ntiles, sizeof *ts->tile))) { nuclear@4: goto failmsg; nuclear@4: } nuclear@4: ts->num_tiles = ntiles; nuclear@4: nuclear@4: i = 0; nuclear@4: while(tlist) { nuclear@4: int idx; nuclear@4: struct orig_node *onode; nuclear@4: struct tile_node *tnode = tlist; nuclear@4: tlist = tlist->next; nuclear@4: nuclear@4: ts->tile[i] = tnode->tile; nuclear@4: free(tnode); nuclear@4: nuclear@4: /* determine the origin index by the tile z number, nuclear@4: * which if it exists, it starts at name[3] nuclear@4: */ nuclear@4: idx = atoi(ts->tile[i].name + 3); nuclear@4: nuclear@4: /* retrieve the origin for this tile */ nuclear@4: if((onode = find_origin(origlist, ts->tile[i].name))) { nuclear@4: if(idx >= onode->count) idx = onode->count - 1; nuclear@4: ts->tile[i].orig_x = onode->orig[idx].x; nuclear@4: ts->tile[i].orig_y = onode->orig[idx].y; nuclear@4: } nuclear@4: ++i; nuclear@4: } nuclear@4: nuclear@4: /* now we need to sort them so that we can binary-search tiles later in real-time */ nuclear@4: qsort(ts->tile, ntiles, sizeof *ts->tile, tilecmp); nuclear@4: nuclear@4: printf("loaded tileset %s\n", fname); nuclear@4: for(i=0; itile[i].name); nuclear@4: } nuclear@4: nuclear@4: return 0; nuclear@4: nuclear@4: failmsg: nuclear@4: fprintf(stderr, "failed to load tileset: %s\n", fname); nuclear@4: fail: nuclear@4: if(fp) fclose(fp); nuclear@4: while(tlist) { nuclear@4: struct tile_node *n = tlist; nuclear@4: tlist = tlist->next; nuclear@4: free(n->tile.name); nuclear@4: destroy_image(&n->tile.img); nuclear@4: free(n); nuclear@4: } nuclear@4: return -1; nuclear@4: } nuclear@4: nuclear@4: void destroy_tileset(struct tileset *ts) nuclear@4: { nuclear@4: int i; nuclear@4: nuclear@4: if(!ts) return; nuclear@4: nuclear@4: if(ts->tile) { nuclear@4: for(i=0; inum_tiles; i++) { nuclear@4: free(ts->tile[i].name); nuclear@4: destroy_image(&ts->tile[i].img); nuclear@4: } nuclear@4: free(ts->tile); nuclear@4: ts->tile = 0; nuclear@4: } nuclear@4: } nuclear@4: nuclear@4: struct tile *get_tile(struct tileset *ts, const char *name) nuclear@4: { nuclear@4: struct tile key; nuclear@4: key.name = (char*)name; nuclear@4: return bsearch(&key, ts->tile, ts->num_tiles, sizeof *ts->tile, tilecmp); nuclear@4: } nuclear@4: nuclear@4: static struct orig_node *load_origin_list(const char *fname) nuclear@4: { nuclear@4: FILE *fp; nuclear@4: char buf[256]; nuclear@4: int i, valbuf[2][64], num, nline = 0; nuclear@4: struct orig_node *head = 0; nuclear@4: nuclear@4: if(!(fp = fopen(fname, "r"))) { nuclear@4: fprintf(stderr, "failed to load origins file: %s: %s\n", fname, strerror(errno)); nuclear@4: return 0; nuclear@4: } nuclear@4: nuclear@4: while(fgets(buf, sizeof buf, fp)) { nuclear@4: char *tok, *name, *line; nuclear@4: struct orig_node *onode; nuclear@4: nuclear@4: ++nline; nuclear@4: nuclear@4: line = clean_line(buf); nuclear@4: if(!*line) continue; nuclear@4: nuclear@4: if(!(name = strtok(line, " \t\n\r"))) { nuclear@4: continue; nuclear@4: } nuclear@4: num = 0; nuclear@4: while(num < 64 && (tok = strtok(0, " \t\n\r"))) { nuclear@4: if(sscanf(tok, "%dx%d", valbuf[0] + num, valbuf[1] + num) != 2) { nuclear@4: fprintf(stderr, "failed to load origins file: %s: ", fname); nuclear@4: fprintf(stderr, "invalid token \"%s\" on line %d (%s)\n", tok, nline, name); nuclear@4: goto fail; nuclear@4: } nuclear@4: ++num; nuclear@4: } nuclear@4: if(num >= 64) { nuclear@4: fprintf(stderr, "warning: origins file %s: excessive number of %s origins on line %d\n", fname, name, nline); nuclear@4: } nuclear@4: nuclear@4: if(!(onode = malloc(sizeof *onode))) { nuclear@4: fprintf(stderr, "failed to allocate origins node\n"); nuclear@4: goto fail; nuclear@4: } nuclear@4: if(!(onode->prefix = malloc(strlen(name) + 1))) { nuclear@4: fprintf(stderr, "failed to allocate origins name\n"); nuclear@4: free(onode); nuclear@4: goto fail; nuclear@4: } nuclear@4: if(!(onode->orig = malloc(num * sizeof *onode->orig))) { nuclear@4: fprintf(stderr, "failed to allocate origins array\n"); nuclear@4: free(onode); nuclear@4: goto fail; nuclear@4: } nuclear@4: nuclear@4: strcpy(onode->prefix, name); nuclear@4: onode->count = num; nuclear@4: onode->next = head; nuclear@4: head = onode; nuclear@4: nuclear@4: for(i=0; iorig[i].x = valbuf[0][i]; nuclear@4: onode->orig[i].y = valbuf[1][i]; nuclear@4: } nuclear@4: } nuclear@4: fclose(fp); nuclear@4: return head; nuclear@4: nuclear@4: fail: nuclear@4: fclose(fp); nuclear@4: while(head) { nuclear@4: struct orig_node *tmp = head; nuclear@4: head = head->next; nuclear@4: free(tmp->prefix); nuclear@4: free(tmp->orig); nuclear@4: } nuclear@4: return 0; nuclear@4: } nuclear@4: nuclear@4: static struct orig_node *find_origin(struct orig_node *list, const char *name) nuclear@4: { nuclear@4: struct orig_node *node = list; nuclear@4: struct orig_node *best = 0; nuclear@4: int best_len = 0; nuclear@4: nuclear@4: while(node) { nuclear@4: const char *nmptr = name; nuclear@4: const char *pptr = node->prefix; nuclear@4: int len; nuclear@4: nuclear@4: while(*pptr && *nmptr && *pptr == *nmptr) ++pptr, ++nmptr; nuclear@4: if((len = nmptr - name) > best_len) { nuclear@4: best = node; nuclear@4: best_len = len; nuclear@4: } nuclear@4: node = node->next; nuclear@4: } nuclear@4: return best; nuclear@4: } nuclear@4: nuclear@4: static char *clean_line(char *buf) nuclear@4: { nuclear@4: char *end = buf + strlen(buf); nuclear@4: nuclear@4: while(*buf && isspace(*buf)) ++buf; nuclear@4: while(end > buf && (!*end || isspace(*end))) { nuclear@4: *end-- = 0; nuclear@4: } nuclear@4: nuclear@4: /* also remove comments */ nuclear@4: if((end = strchr(buf, '#'))) { nuclear@4: *end = 0; nuclear@4: } nuclear@4: return buf; nuclear@4: } nuclear@4: nuclear@4: static struct option *get_option(const char *name) nuclear@4: { nuclear@4: int i; nuclear@4: for(i=0; opt[i].name; i++) { nuclear@4: if(strcmp(name, opt[i].name) == 0) { nuclear@4: assert(i == opt[i].id); nuclear@4: return opt + i; nuclear@4: } nuclear@4: } nuclear@4: return 0; nuclear@4: } nuclear@4: nuclear@4: /* tile comparison for sorting the array of tiles by name */ nuclear@4: static int tilecmp(const void *a, const void *b) nuclear@4: { nuclear@4: const struct tile *ta = a; nuclear@4: const struct tile *tb = b; nuclear@4: return strcmp(ta->name, tb->name); nuclear@4: }