# HG changeset patch # User John Tsiombikas # Date 1421580630 -7200 # Node ID ce0548d24918f2a9e014fd426fc01163ba50a21e # Parent e32bdd5fb622a9463efc9d9e635ddec72fb32d42 mostly works diff -r e32bdd5fb622 -r ce0548d24918 src/fblib.c --- a/src/fblib.c Sun Jan 18 05:51:51 2015 +0200 +++ b/src/fblib.c Sun Jan 18 13:30:30 2015 +0200 @@ -29,19 +29,19 @@ return 0; } -void fb_keyboard_callback(void (*func)(int, int, void*), void *cls) +void fb_keyboard_callback(int (*func)(int, int, void*), void *cls) { fb_cb.keyb = func; fb_cb.keyb_data = cls; } -void fb_mouse_button_callback(void (*func)(int, int, int, int, void*), void *cls) +void fb_mouse_button_callback(int (*func)(int, int, int, int, void*), void *cls) { fb_cb.button = func; fb_cb.button_data = cls; } -void fb_mouse_motion_callback(void (*func)(int, int, void*), void *cls) +void fb_mouse_motion_callback(int (*func)(int, int, void*), void *cls) { fb_cb.motion = func; fb_cb.motion_data = cls; diff -r e32bdd5fb622 -r ce0548d24918 src/fblib.h --- a/src/fblib.h Sun Jan 18 05:51:51 2015 +0200 +++ b/src/fblib.h Sun Jan 18 13:30:30 2015 +0200 @@ -30,9 +30,9 @@ int fb_key_state(int key); int fb_mouse_state(int bn, int *x, int *y); -void fb_keyboard_callback(void (*func)(int, int, void*), void *cls); -void fb_mouse_button_callback(void (*func)(int, int, int, int, void*), void *cls); -void fb_mouse_motion_callback(void (*func)(int, int, void*), void *cls); +void fb_keyboard_callback(int (*func)(int, int, void*), void *cls); +void fb_mouse_button_callback(int (*func)(int, int, int, int, void*), void *cls); +void fb_mouse_motion_callback(int (*func)(int, int, void*), void *cls); int fb_process_events(void); diff -r e32bdd5fb622 -r ce0548d24918 src/fblibimp.h --- a/src/fblibimp.h Sun Jan 18 05:51:51 2015 +0200 +++ b/src/fblibimp.h Sun Jan 18 13:30:30 2015 +0200 @@ -4,9 +4,9 @@ #include "fblib.h" struct callbacks { - void (*keyb)(int, int, void*); - void (*button)(int, int, int, int, void*); - void (*motion)(int, int, void*); + int (*keyb)(int, int, void*); + int (*button)(int, int, int, int, void*); + int (*motion)(int, int, void*); void *keyb_data, *button_data, *motion_data; } fb_cb; diff -r e32bdd5fb622 -r ce0548d24918 src/fblibsdl.c --- a/src/fblibsdl.c Sun Jan 18 05:51:51 2015 +0200 +++ b/src/fblibsdl.c Sun Jan 18 13:30:30 2015 +0200 @@ -155,7 +155,9 @@ fb_inp.key[key] = state; } if(fb_cb.keyb) { - fb_cb.keyb(key, state, fb_cb.keyb_data); + if(fb_cb.keyb(key, state, fb_cb.keyb_data) == -1) { + return -1; + } } else { if(key == SDLK_ESCAPE) { return -1; @@ -173,7 +175,9 @@ fb_inp.mbutton[ev.button.which] = state; if(fb_cb.button) { - fb_cb.button(ev.button.which, state, fb_inp.mx, fb_inp.my, fb_cb.button_data); + if(fb_cb.button(ev.button.which, state, fb_inp.mx, fb_inp.my, fb_cb.button_data) == -1) { + return -1; + } } } break; @@ -182,7 +186,9 @@ fb_inp.mx = ev.motion.x / scale; fb_inp.my = ev.motion.y / scale; if(fb_cb.motion) { - fb_cb.motion(fb_inp.mx, fb_inp.my, fb_cb.motion_data); + if(fb_cb.motion(fb_inp.mx, fb_inp.my, fb_cb.motion_data) == -1) { + return -1; + } } break; } diff -r e32bdd5fb622 -r ce0548d24918 src/image.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/image.c Sun Jan 18 13:30:30 2015 +0200 @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include "image.h" + +#define LOADFAIL(f, m) fprintf(stderr, "failed to load image: %s: %s\n", f, m) + +int load_image(struct image *img, const char *fname) +{ + static const char *magic = "TILEIMAG"; + FILE *fp; + char sig[8]; + + if(!(fp = fopen(fname, "rb"))) { + LOADFAIL(fname, strerror(errno)); + return -1; + } + if(fread(sig, sizeof sig, 1, fp) <= 0 || memcmp(sig, magic, 8) != 0) { + LOADFAIL(fname, "corrupted or empty file"); + fclose(fp); + return -1; + } + if(fread(&img->xsz, 4, 1, fp) <= 0 || fread(&img->ysz, 4, 1, fp) <= 0) { + LOADFAIL(fname, "failed to read file header"); + fclose(fp); + return -1; + } + + if(!(img->pixels = malloc(img->xsz * img->ysz))) { + LOADFAIL(fname, "failed to allocate pixel buffer"); + fclose(fp); + return -1; + } + if(fread(img->pixels, 1, img->xsz * img->ysz, fp) < img->xsz * img->ysz) { + LOADFAIL(fname, "unexpected end of file while reading pixels"); + fclose(fp); + return -1; + } + fclose(fp); + return 0; +} + +void destroy_image(struct image *img) +{ + if(img) { + free(img->pixels); + img->pixels = 0; + } +} + +int load_palette(struct color *col, const char *fname) +{ + FILE *fp; + char buf[128]; + int nent = 0; + + if(!(fp = fopen(fname, "r"))) { + fprintf(stderr, "failed to open palette file: %s: %s\n", fname, strerror(errno)); + return -1; + } + + while(fgets(buf, sizeof buf, fp)) { + char *endp, *line = buf; + int r, g, b; + + if(!line || !*line) continue; + + if(*line == '#') { /* hex html-like values */ + unsigned int val = strtol(line + 1, &endp, 16); + if(endp == line) { + fprintf(stderr, "unrecognized line \"%s\" in palette file: %s\n", line, fname); + fclose(fp); + return -1; + } + + r = (val >> 16) & 0xff; + g = (val >> 8) & 0xff; + b = val & 0xff; + } else { + fprintf(stderr, "unrecognized line \"%s\" in palette file: %s\n", line, fname); + fclose(fp); + return -1; + } + + if(nent >= 256) { + fprintf(stderr, "palette file %s contains more than 256 entries ... skipping the rest\n", fname); + break; + } + + col[nent].r = r; + col[nent].g = g; + col[nent].b = b; + nent++; + } + + printf("loaded palette: %s (%d colors)\n", fname, nent); + + fclose(fp); + return nent; +} + +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +void blitkey(struct image *destimg, int dstx, int dsty, struct image *srcimg, int key) +{ + int srcx = 0, srcy = 0; + int i, j, width, height; + unsigned char *src, *dst; + + if(dstx < 0) { + srcx += -dstx; + dstx = 0; + } + if(dsty < 0) { + srcy += -dsty; + dsty = 0; + } + + width = MIN(destimg->xsz - dstx, srcimg->xsz - srcx); + height = MIN(destimg->ysz - dsty, srcimg->ysz - srcy); + + if(width <= 0 || height <= 0) return; /* ended up with a zero-area blit */ + + src = srcimg->pixels + srcy * srcimg->xsz + srcx; + dst = destimg->pixels + dsty * destimg->xsz + dstx; + + for(i=0; ixsz; + dst += destimg->xsz; + } +} diff -r e32bdd5fb622 -r ce0548d24918 src/image.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/image.h Sun Jan 18 13:30:30 2015 +0200 @@ -0,0 +1,21 @@ +#ifndef IMAGE_H_ +#define IMAGE_H_ + +struct color { + int r, g, b; +}; + +struct image { + int xsz, ysz; + unsigned char *pixels; +}; + +int load_image(struct image *img, const char *fname); +void destroy_image(struct image *img); + +/* returns the number of palette entries loaded */ +int load_palette(struct color *col, const char *fname); + +void blitkey(struct image *img, int x, int y, struct image *src, int key); + +#endif /* IMAGE_H_ */ diff -r e32bdd5fb622 -r ce0548d24918 src/level.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/level.c Sun Jan 18 13:30:30 2015 +0200 @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include +#include "level.h" + +#define C_WALL '#' +#define C_START 's' +#define C_GOAL 'x' + +#define IS_SOLID(x) ((x) == C_WALL) + +static void clean_line(char *buf); + + +void level_init(struct level *lvl) +{ + memset(lvl, 0, sizeof *lvl); +} + +void level_destroy(struct level *lvl) +{ + if(lvl) { + destroy_tileset(&lvl->tileset); + } +} + +int level_load(struct level *lvl, const char *fname) +{ + FILE *fp; + char buf[256], *dir, *slash; + int i, size[2], nlines; + + dir = alloca(strlen(fname) + 1); + strcpy(dir, fname); + if((slash = strrchr(dir, '/'))) { + slash[1] = 0; + } else { + *dir = 0; + } + + if(!(fp = fopen(fname, "r"))) { + fprintf(stderr, "failed to open file: %s\n", fname); + return -1; + } + + nlines = 0; + while(fgets(buf, sizeof buf, fp)) { + clean_line(buf); + + if(memcmp(buf, "@s", 2) == 0) { + if(sscanf(buf, "@s %dx%d", size, size + 1) != 2) { + fprintf(stderr, "level file %s doesn't start with size definition\n", fname); + fclose(fp); + return -1; + } + if(size[0] > MAX_LEVEL_SIZE || size[1] > MAX_LEVEL_SIZE) { + fprintf(stderr, "level size %dx%d is larger than compile-time maximum (%d)\n", size[0], size[1], MAX_LEVEL_SIZE); + fclose(fp); + return -1; + } + + lvl->num_cells[0] = size[0]; + lvl->num_cells[1] = size[1]; + continue; + + } else if(memcmp(buf, "@t", 2) == 0) { + char *path, *tname = buf + 3; + + while(*tname && isspace(*tname)) ++tname; + + path = alloca(strlen(tname) + strlen(dir) + 2); + sprintf(path, "%s%s", dir, tname); + + if(load_tileset(&lvl->tileset, path) == -1) { + fprintf(stderr, "failed to load tileset for level %s\n", fname); + fclose(fp); + return -1; + } + continue; + } + + if(nlines >= size[0]) { + fprintf(stderr, "warning: level contains more lines than specified, ignoring the rest\n"); + break; + } + + for(i=0; buf[i]; i++) { + if(i >= size[1]) { + fprintf(stderr, "warning: line %d is longer than the level size definition says. Skipping the rest.\n", nlines + 1); + break; + } + lvl->cells[nlines][i] = buf[i]; + + if(buf[i] == C_START) { + lvl->start_pos[0] = i; + lvl->start_pos[1] = nlines; + printf("start cell found (%d,%d)\n", lvl->start_pos[0], lvl->start_pos[1]); + } + if(buf[i] == C_GOAL) { + lvl->goal_pos[0] = i; + lvl->goal_pos[1] = nlines; + printf("gold cell found (%d, %d)\n", i, nlines); + } + } + nlines++; + } + + fclose(fp); + return 0; +} + +static int clamp(int x, int low, int high) +{ + return x < low ? low : (x > high ? high : x); +} + +int level_cell(struct level *lvl, int cx, int cy) +{ + cx = clamp(cx, 0, lvl->num_cells[1] - 1); + cy = clamp(cy, 0, lvl->num_cells[0] - 1); + + return lvl->cells[cy][cx]; +} + + +static void clean_line(char *buf) +{ + char *end = buf + strlen(buf) - 1; + + if(end <= buf) return; + + while(end >= buf && !isprint(*end)) { + *end-- = 0; + } +} + diff -r e32bdd5fb622 -r ce0548d24918 src/level.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/level.h Sun Jan 18 13:30:30 2015 +0200 @@ -0,0 +1,24 @@ +#ifndef LEVEL_H_ +#define LEVEL_H_ + +#include "tileset.h" + +#define MAX_LEVEL_SIZE 64 + +struct level { + int cells[MAX_LEVEL_SIZE][MAX_LEVEL_SIZE]; + int num_cells[2]; + int start_pos[2]; + int goal_pos[2]; + + struct tileset tileset; +}; + +void level_init(struct level *lvl); +void level_destroy(struct level *lvl); + +int level_load(struct level *lvl, const char *fname); + +int level_cell(struct level *lvl, int cx, int cy); + +#endif /* LEVEL_H_ */ diff -r e32bdd5fb622 -r ce0548d24918 src/main.c --- a/src/main.c Sun Jan 18 05:51:51 2015 +0200 +++ b/src/main.c Sun Jan 18 13:30:30 2015 +0200 @@ -1,14 +1,22 @@ #include #include "fblib.h" +#include "rend.h" +#include "level.h" +#include "player.h" -static void display(); +static int init(void); +static void cleanup(void); +static void display(void); +static int keyb(int key, int state, void *cls); + +static struct player player; +static struct level level; int main(int argc, char **argv) { - if(fb_init(320, 240, 8) == -1) { + if(init() == -1) { return 1; } - fb_set_palette_entry(1, 255, 0, 0); for(;;) { if(fb_process_events() == -1) { @@ -17,21 +25,95 @@ display(); } -done: - fb_shutdown(); + cleanup(); return 0; } -void display() +static int init(void) { int i; + + if(fb_init(320, 240, 8) == -1) { + return -1; + } + fb_keyboard_callback(keyb, 0); + + level_init(&level); + if(level_load(&level, "data/0.lvl") == -1) { + return -1; + } + + if(init_renderer() == -1) { + return -1; + } + + player_init(&player, &level); + + /* setup the level tileset palette + * TODO: do it properly + */ + for(i=0; ir, col->g, col->b); + } + + return 0; +} + +static void cleanup(void) +{ + shutdown_renderer(); + level_destroy(&level); + fb_shutdown(); +} + +static void display(void) +{ int width = fb_get_width(); int height = fb_get_height(); unsigned char *pixels = (unsigned char*)fb_begin_frame(); - for(i=0; i +#include "player.h" +#include "level.h" + +static char *dirnames[] = { + "north", "east", "south", "west" +}; + +void player_init(struct player *p, struct level *lvl) +{ + p->x = lvl->start_pos[0]; + p->y = lvl->start_pos[1]; + p->lvl = lvl; + p->dir = DIR_NORTH; +} + +int player_move(struct player *p, int dfwd, int dside) +{ + int nx, ny; + int dx, dy; + + player_dir_vec(p, &dx, &dy); + nx = p->x + dx * dfwd - dy * dside; + ny = p->y + dy * dfwd + dx * dside; + + if(level_cell(p->lvl, nx, ny) == '#') { + return -1; + } + p->x = nx; + p->y = ny; + return 0; +} + +void player_turn(struct player *p, int angle) +{ + int dir = (p->dir + angle) % 4; + if(dir < 0) dir += 4; + + printf("turn(%d): %s -> %s\n", angle, dirnames[p->dir], dirnames[dir]); + p->dir = dir; +} + +void player_dir_vec(struct player *p, int *dx, int *dy) +{ + *dx = p->dir == DIR_EAST ? 1 : (p->dir == DIR_WEST ? -1 : 0); + *dy = p->dir == DIR_NORTH ? -1 : (p->dir == DIR_SOUTH ? 1 : 0); +} diff -r e32bdd5fb622 -r ce0548d24918 src/player.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/player.h Sun Jan 18 13:30:30 2015 +0200 @@ -0,0 +1,23 @@ +#ifndef PLAYER_H_ +#define PLAYER_H_ + +enum { DIR_NORTH, DIR_EAST, DIR_SOUTH, DIR_WEST }; + +struct level; + +struct player { + int x, y; + int dir; + + struct level *lvl; +}; + +void player_init(struct player *p, struct level *lvl); + +int player_move(struct player *p, int dfwd, int dside); +void player_turn(struct player *p, int angle); + +void player_dir_vec(struct player *p, int *dx, int *dy); + + +#endif /* PLAYER_H_ */ diff -r e32bdd5fb622 -r ce0548d24918 src/rend.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rend.c Sun Jan 18 13:30:30 2015 +0200 @@ -0,0 +1,119 @@ +#include +#include +#include "rend.h" +#include "player.h" + +static void draw_tile(struct tile *tile); + +static struct image fb; + + +int init_renderer(void) +{ + return 0; +} + +void shutdown_renderer(void) +{ +} + +void setup_renderer(unsigned char *pixels, int xsz, int ysz) +{ + fb.xsz = xsz; + fb.ysz = ysz; + fb.pixels = pixels; +} + +#define MAX_Z 5 +#define LEFT 0 +#define CENTER 1 +#define RIGHT 2 + +#define CHR_TYPE 0 +#define CHR_SIDE 1 +#define CHR_STATE 2 +#define CHR_Z 3 + +void render_level(struct level *lvl, int px, int py, int pdir) +{ + int i, j, dx, dy, backz, clear_start, clear_end; + char cells[3][MAX_Z + 1]; + struct tileset *ts = &lvl->tileset; + struct tile *tile_ceil, *tile_floor; + + dx = pdir == DIR_EAST ? 1 : (pdir == DIR_WEST ? -1 : 0); + dy = pdir == DIR_NORTH ? -1 : (pdir == DIR_SOUTH ? 1 : 0); + + for(i=0; i<3; i++) { /* get 3 abreast cells ... */ + int xoffs = (1 - i) * dy; + int yoffs = (i - 1) * dx; + + for(j=0; jorig_y + tile_ceil->img.ysz : 0; + clear_end = tile_floor ? tile_floor->orig_y : fb.ysz; + + draw_tile(tile_ceil); + draw_tile(tile_floor); + memset(fb.pixels + clear_start * fb.xsz, 0, (clear_end - clear_start) * fb.xsz); + + /* find where the back wall should go if it is within visual range + * and draw it ... + */ + backz = -1; + for(i=1; i-- */ + + for(j=0; j<3; j++) { + int state = 's'; + + if(j == CENTER) continue; /* no walls in the center */ + + if(cells[j][z] != '#') { + state = 'o'; + /* if it's an open cell, then skip drawing if the next z is also open */ + if(z >= MAX_Z || cells[j][z + 1] != '#') continue; + } + + name[CHR_SIDE] = j == LEFT ? 'l' : 'r'; + name[CHR_STATE] = state; + name[CHR_Z] = z + '0'; + draw_tile(get_tile(ts, name)); + } + } +} + +static void draw_tile(struct tile *tile) +{ + if(!tile) return; + + blitkey(&fb, tile->orig_x, tile->orig_y, &tile->img, 31); /* TODO unhardcode key */ +} diff -r e32bdd5fb622 -r ce0548d24918 src/rend.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/rend.h Sun Jan 18 13:30:30 2015 +0200 @@ -0,0 +1,14 @@ +#ifndef REND_H_ +#define REND_H_ + +#include "level.h" + +int init_renderer(void); +void shutdown_renderer(void); + +void setup_renderer(unsigned char *pixels, int xsz, int ysz); + +void render_level(struct level *lvl, int px, int py, int pdir); + + +#endif /* REND_H_ */ diff -r e32bdd5fb622 -r ce0548d24918 src/tileset.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tileset.c Sun Jan 18 13:30:30 2015 +0200 @@ -0,0 +1,374 @@ +#include +#include +#include +#include +#include +#include +#include "tileset.h" +#include "image.h" + +struct orig_node { + char *prefix; + struct { int x, y; } *orig; + int count; + struct orig_node *next; +}; + +struct tile_node { + struct tile tile; + struct tile_node *next; +}; + +enum { OPT_IMGDIR, OPT_MAXDIST, OPT_ORIGINS, OPT_PALETTE }; +static struct option { + int id; + char *name, *val; + int ival; +} opt[] = { + /* maintain the same order as with the enum above */ + { OPT_IMGDIR, "imagedir", 0, 0 }, + { OPT_MAXDIST, "maxdist", 0, 0 }, + { OPT_ORIGINS, "origins", 0, 0 }, + { OPT_PALETTE, "palette", 0, 0 }, + { 0, 0, 0 } +}; + +static struct orig_node *load_origin_list(const char *fname); +static struct orig_node *find_origin(struct orig_node *list, const char *name); +static char *clean_line(char *buf); +static struct option *get_option(const char *name); +static int tilecmp(const void *a, const void *b); + +int load_tileset(struct tileset *ts, const char *fname) +{ + FILE *fp; + char buf[256]; + int i, nline = 0, ntiles = 0; + struct orig_node *origlist = 0; + struct tile_node *tlist = 0; + char *slash, *dir, *imgdir = ""; + + dir = alloca(strlen(fname) + 1); + strcpy(dir, fname); + if((slash = strrchr(dir, '/'))) { + slash[1] = 0; + } else { + *dir = 0; + } + + if(!(fp = fopen(fname, "r"))) { + fprintf(stderr, "failed to load tileset: %s: %s\n", fname, strerror(errno)); + return -1; + } + + while(fgets(buf, sizeof buf, fp)) { + char *line, *name, *valstr; + struct option *opt; + + ++nline; + + line = clean_line(buf); + if(!*line) continue; + + name = line; + if(!(valstr = strchr(line, '='))) { + fprintf(stderr, "tileset: %s line %d: invalid syntax: %s\n", fname, nline, line); + goto fail; + } + *valstr++ = 0; + + name = clean_line(name); + valstr = clean_line(valstr); + + if(!*name || !*valstr) { + fprintf(stderr, "tileset: %s line %d: malformed line\n", fname, nline); + goto fail; + } + + /* first check to see if it's an option */ + if((opt = get_option(name))) { + opt->val = strdup(valstr); + opt->ival = atoi(valstr); + + switch(opt->id) { + case OPT_IMGDIR: + { + int len = strlen(opt->val); + imgdir = alloca(len + 2); + strcpy(imgdir, opt->val); + + if(imgdir[len - 1] != '/') { + imgdir[len] = '/'; + imgdir[len + 1] = 0; + } + printf("imgdir: \"%s\"\n", imgdir); + } + break; + + case OPT_ORIGINS: + { + char *path = alloca(strlen(dir) + strlen(opt->val) + 1); + sprintf(path, "%s%s", dir, opt->val); + + if(!(origlist = load_origin_list(path))) { + goto failmsg; + } + } + default: + break; + } + + } else { + /* otherwise it's just a tile... add it to the list */ + char *path; + struct tile_node *tnode; + + if(!(tnode = malloc(sizeof *tnode))) { + goto failmsg; + } + if(!(tnode->tile.name = strdup(name))) { + free(tnode); + goto failmsg; + } + + if(!(path = malloc(strlen(dir) + strlen(imgdir) + strlen(valstr) + 1))) { + free(tnode->tile.name); + free(tnode); + goto failmsg; + } + + sprintf(path, "%s%s%s", dir, imgdir, valstr); + if(load_image(&tnode->tile.img, path) == -1) { + free(tnode->tile.name); + free(tnode); + free(path); + goto failmsg; + } + free(path); + tnode->next = tlist; + tlist = tnode; + ++ntiles; + } + } + fclose(fp), fp = 0; + + /* if there is a palette, load it */ + if(opt[OPT_PALETTE].val) { + const char *fname = opt[OPT_PALETTE].val; + char *path = alloca(strlen(dir) + strlen(fname) + 1); + sprintf(path, "%s%s", dir, fname); + + if((ts->pal_size = load_palette(ts->pal, path)) == -1) { + goto failmsg; + } + } + + /* allocate the array of tiles and copy all the tiles from the list */ + if(!(ts->tile = calloc(ntiles, sizeof *ts->tile))) { + goto failmsg; + } + ts->num_tiles = ntiles; + + i = 0; + while(tlist) { + int idx; + struct orig_node *onode; + struct tile_node *tnode = tlist; + tlist = tlist->next; + + ts->tile[i] = tnode->tile; + free(tnode); + + /* determine the origin index by the tile z number, + * which if it exists, it starts at name[3] + */ + idx = atoi(ts->tile[i].name + 3); + + /* retrieve the origin for this tile */ + if((onode = find_origin(origlist, ts->tile[i].name))) { + if(idx >= onode->count) idx = onode->count - 1; + ts->tile[i].orig_x = onode->orig[idx].x; + ts->tile[i].orig_y = onode->orig[idx].y; + } + ++i; + } + + /* now we need to sort them so that we can binary-search tiles later in real-time */ + qsort(ts->tile, ntiles, sizeof *ts->tile, tilecmp); + + printf("loaded tileset %s\n", fname); + for(i=0; itile[i].name); + } + + return 0; + +failmsg: + fprintf(stderr, "failed to load tileset: %s\n", fname); +fail: + if(fp) fclose(fp); + while(tlist) { + struct tile_node *n = tlist; + tlist = tlist->next; + free(n->tile.name); + destroy_image(&n->tile.img); + free(n); + } + return -1; +} + +void destroy_tileset(struct tileset *ts) +{ + int i; + + if(!ts) return; + + if(ts->tile) { + for(i=0; inum_tiles; i++) { + free(ts->tile[i].name); + destroy_image(&ts->tile[i].img); + } + free(ts->tile); + ts->tile = 0; + } +} + +struct tile *get_tile(struct tileset *ts, const char *name) +{ + struct tile key; + key.name = (char*)name; + return bsearch(&key, ts->tile, ts->num_tiles, sizeof *ts->tile, tilecmp); +} + +static struct orig_node *load_origin_list(const char *fname) +{ + FILE *fp; + char buf[256]; + int i, valbuf[2][64], num, nline = 0; + struct orig_node *head = 0; + + if(!(fp = fopen(fname, "r"))) { + fprintf(stderr, "failed to load origins file: %s: %s\n", fname, strerror(errno)); + return 0; + } + + while(fgets(buf, sizeof buf, fp)) { + char *tok, *name, *line; + struct orig_node *onode; + + ++nline; + + line = clean_line(buf); + if(!*line) continue; + + if(!(name = strtok(line, " \t\n\r"))) { + continue; + } + num = 0; + while(num < 64 && (tok = strtok(0, " \t\n\r"))) { + if(sscanf(tok, "%dx%d", valbuf[0] + num, valbuf[1] + num) != 2) { + fprintf(stderr, "failed to load origins file: %s: ", fname); + fprintf(stderr, "invalid token \"%s\" on line %d (%s)\n", tok, nline, name); + goto fail; + } + ++num; + } + if(num >= 64) { + fprintf(stderr, "warning: origins file %s: excessive number of %s origins on line %d\n", fname, name, nline); + } + + if(!(onode = malloc(sizeof *onode))) { + fprintf(stderr, "failed to allocate origins node\n"); + goto fail; + } + if(!(onode->prefix = malloc(strlen(name) + 1))) { + fprintf(stderr, "failed to allocate origins name\n"); + free(onode); + goto fail; + } + if(!(onode->orig = malloc(num * sizeof *onode->orig))) { + fprintf(stderr, "failed to allocate origins array\n"); + free(onode); + goto fail; + } + + strcpy(onode->prefix, name); + onode->count = num; + onode->next = head; + head = onode; + + for(i=0; iorig[i].x = valbuf[0][i]; + onode->orig[i].y = valbuf[1][i]; + } + } + fclose(fp); + return head; + +fail: + fclose(fp); + while(head) { + struct orig_node *tmp = head; + head = head->next; + free(tmp->prefix); + free(tmp->orig); + } + return 0; +} + +static struct orig_node *find_origin(struct orig_node *list, const char *name) +{ + struct orig_node *node = list; + struct orig_node *best = 0; + int best_len = 0; + + while(node) { + const char *nmptr = name; + const char *pptr = node->prefix; + int len; + + while(*pptr && *nmptr && *pptr == *nmptr) ++pptr, ++nmptr; + if((len = nmptr - name) > best_len) { + best = node; + best_len = len; + } + node = node->next; + } + return best; +} + +static char *clean_line(char *buf) +{ + char *end = buf + strlen(buf); + + while(*buf && isspace(*buf)) ++buf; + while(end > buf && (!*end || isspace(*end))) { + *end-- = 0; + } + + /* also remove comments */ + if((end = strchr(buf, '#'))) { + *end = 0; + } + return buf; +} + +static struct option *get_option(const char *name) +{ + int i; + for(i=0; opt[i].name; i++) { + if(strcmp(name, opt[i].name) == 0) { + assert(i == opt[i].id); + return opt + i; + } + } + return 0; +} + +/* tile comparison for sorting the array of tiles by name */ +static int tilecmp(const void *a, const void *b) +{ + const struct tile *ta = a; + const struct tile *tb = b; + return strcmp(ta->name, tb->name); +} diff -r e32bdd5fb622 -r ce0548d24918 src/tileset.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/tileset.h Sun Jan 18 13:30:30 2015 +0200 @@ -0,0 +1,25 @@ +#ifndef TILESET_H_ +#define TILESET_H_ + +#include "image.h" + +struct tile { + char *name; + struct image img; + int orig_x, orig_y; +}; + +struct tileset { + struct tile *tile; + int num_tiles; + + struct color pal[256]; + int pal_size; +}; + +int load_tileset(struct tileset *ts, const char *fname); +void destroy_tileset(struct tileset *ts); + +struct tile *get_tile(struct tileset *ts, const char *name); + +#endif /* TILESET_H_ */