#include #include #include #include #include #include "map.h" #include "resource.h" #include "../xml.h" #include "../logging.h" struct resource_table ts_res; struct resource_table map_res; static struct tileset *tileset_get(const char *, const char *); static void XMLCALL tmx_map_start(void *, const char *, const char **); static void XMLCALL tmx_map_end(void *, const char *); static void XMLCALL tmx_map_el_start(void *, const char *, const char **); static void XMLCALL tmx_tileset_start(void *, const char *, const char **); static void XMLCALL tmx_tileset_end(void *, const char *); static void XMLCALL tmx_tilesheetemb_end(void *, const char *); static void XMLCALL tmx_tileset_el_start(void *, const char *, const char **); static void XMLCALL tmx_image_end(void *, const char *); static void XMLCALL tmx_unused_start(void *, const char *, const char **); static void XMLCALL tmx_unused_end(void *, const char *); static void XMLCALL tmx_invalid_start(void *, const char *, const char **); static void XMLCALL tmx_invalid_end(void *, const char *); struct map * map_get(const char *path) { struct map *map; XML_Parser p; FILE *tmx_fp; void *tmx_buf; size_t len; enum XML_Status status; map = (struct map *) resource_get(&map_res, path); if (map != NULL) { resource_use((struct resource *) map); return map; } map = resource_alloc(path, sizeof(*map)); if (map == NULL) { return NULL; } map->dirname = dirname(strdup(path)); p = XML_ParserCreate(NULL); if (p == NULL) { warn("Failed to create TMX parser"); return NULL; } XML_UseParserAsHandlerArg(p); xml_node_push(p, map, tmx_map_start, tmx_invalid_end, NULL); tmx_fp = fopen(path, "rb"); if (tmx_fp == NULL) { warn("Failed to open TMX file"); xml_node_pop(p); XML_ParserFree(p); return NULL; } tmx_buf = XML_GetBuffer(p, 8192); if (tmx_buf == NULL) { warn("Failed to create TMX parse buffer"); xml_node_pop(p); XML_ParserFree(p); fclose(tmx_fp); return NULL; } while (!feof(tmx_fp)) { len = fread(tmx_buf, 1, 8192, tmx_fp); status = XML_ParseBuffer(p, len, feof(tmx_fp)); if (status == XML_STATUS_OK) { continue; } warn("Failed to parse TMX file (%s)", XML_ErrorString(XML_GetErrorCode(p))); xml_node_pop(p); XML_ParserFree(p); fclose(tmx_fp); return NULL; } XML_ParserFree(p); fclose(tmx_fp); resource_use((struct resource *) map); resource_add(&map_res, path, (struct resource *) map); return map; } void map_add_layer(struct map *m, struct layer *l) { if (m->layers_head == NULL) { m->layers_head = l; } else { m->layers_tail->next = l; } m->layers_tail = l; } void map_add_tileset(struct map *m, struct tileset *t) { if (m->tilesets_head == NULL) { m->tilesets_head = t; } else { m->tilesets_tail->next = t; } m->tilesets_tail = t; } static struct tileset * tileset_get(const char *path, const char *basedir) { struct tileset *ts; XML_Parser p; FILE *tmx_fp; void *tmx_buf; size_t len; enum XML_Status status; ts = (struct tileset *) resource_get(&ts_res, path); if (ts != NULL) { resource_use((struct resource *) ts); return ts; } ts = resource_alloc(path, sizeof(*ts)); if (ts == NULL) { return NULL; } ts->dirname = strdup(basedir); p = XML_ParserCreate(NULL); if (p == NULL) { warn("Failed to create TSX parser"); return NULL; } XML_UseParserAsHandlerArg(p); xml_node_push(p, ts, tmx_tileset_start, tmx_invalid_end, NULL); tmx_fp = fopen(path, "rb"); if (tmx_fp == NULL) { warn("Failed to open TSX file"); xml_node_pop(p); XML_ParserFree(p); return NULL; } tmx_buf = XML_GetBuffer(p, 8192); if (tmx_buf == NULL) { warn("Failed to create TSX parse buffer"); xml_node_pop(p); XML_ParserFree(p); fclose(tmx_fp); return NULL; } while (!feof(tmx_fp)) { len = fread(tmx_buf, 1, 8192, tmx_fp); status = XML_ParseBuffer(p, len, feof(tmx_fp)); if (status == XML_STATUS_OK) { continue; } warn("Failed to parse TSX file (%s)", XML_ErrorString(XML_GetErrorCode(p))); xml_node_pop(p); XML_ParserFree(p); fclose(tmx_fp); return NULL; } XML_ParserFree(p); fclose(tmx_fp); resource_use((struct resource *) ts); resource_add(&ts_res, path, (struct resource *) ts); return ts; } static void XMLCALL tmx_map_start(void *pv, const char *name, const char **attr) { XML_Parser p = (XML_Parser) pv; struct map *m; debug("<%s> (map)", name); if (xml_check_tag(name, "map")) { m = xml_node_peek(p); xml_get_int_attr(p, attr, "width", &m->width, 1); xml_get_int_attr(p, attr, "height", &m->height, 1); xml_get_int_attr(p, attr, "tilewidth", &m->tilewidth, 1); xml_get_int_attr(p, attr, "tileheight", &m->tileheight, 1); xml_node_push(p, m, tmx_map_el_start, tmx_map_end, NULL); } else { xml_unexpected_start_tag(p, name, "map"); } } static void XMLCALL tmx_map_end(void *pv, const char *name) { XML_Parser p = (XML_Parser) pv; debug(" (map)", name); if (xml_check_tag(name, "map")) { xml_node_pop(p); } else { xml_unexpected_end_tag(p, name, "map"); } } static void XMLCALL tmx_map_el_start(void *pv, const char *name, const char **attr) { XML_Parser p = (XML_Parser) pv; char *dirname; char *source; char *path; struct tileset *ts; debug("<%s> (map child)", name); if (xml_check_tag(name, "properties")) { /* Not used by engine. */ xml_node_push(p, NULL, tmx_unused_start, tmx_unused_end, NULL); } else if (xml_check_tag(name, "tileset")) { dirname = ((struct map *) xml_node_peek(p))->dirname; source = NULL; xml_get_string_attr(p, attr, "source", &source, 0); if (source != NULL) { /* External tilesheet. */ path = malloc(strlen(dirname) + strlen(source) + 2); if (path == NULL) { return; } sprintf(path, "%s/%s", dirname, source); ts = tileset_get(path, dirname); free(source); xml_node_push(p, ts, tmx_invalid_start, tmx_tilesheetemb_end, NULL); } else { /* Embedded tilesheet. */ ts = resource_alloc("internal", sizeof(*ts)); if (ts == NULL) { return; } ts->dirname = dirname; xml_get_string_attr(p, attr, "name", &ts->name, 1); xml_get_int_attr(p, attr, "tilewidth", &ts->tilewidth, 1); xml_get_int_attr(p, attr, "tileheight", &ts->tileheight, 1); xml_node_push(p, ts, tmx_tileset_el_start, tmx_tilesheetemb_end, NULL); } xml_get_int_attr(p, attr, "firstgid", &ts->firstgid, 1); } else { xml_unexpected_start_tag(p, name, "properties, tileset, layer, or objectgroup"); } } static void XMLCALL tmx_tileset_start(void *pv, const char *name, const char **attr) { XML_Parser p = (XML_Parser) pv; struct tileset *ts; debug("<%s> (tileset)", name); if (xml_check_tag(name, "tileset")) { ts = xml_node_peek(p); xml_get_string_attr(p, attr, "name", &ts->name, 1); xml_get_int_attr(p, attr, "tilewidth", &ts->tilewidth, 1); xml_get_int_attr(p, attr, "tileheight", &ts->tileheight, 1); xml_node_push(p, ts, tmx_tileset_el_start, tmx_tileset_end, NULL); } else { xml_unexpected_start_tag(p, name, "tileset"); } } static void XMLCALL tmx_tileset_end(void *pv, const char *name) { XML_Parser p = (XML_Parser) pv; debug(" (external tileset)", name); if (xml_check_tag(name, "tileset")) { xml_node_pop(p); } else { xml_unexpected_end_tag(p, name, "tileset"); } } static void XMLCALL tmx_tilesheetemb_end(void *pv, const char *name) { XML_Parser p = (XML_Parser) pv; struct tileset *ts; debug(" (tileset)", name); if (xml_check_tag(name, "tileset")) { ts = (struct tileset *) xml_node_pop(p); map_add_tileset((struct map *) xml_node_peek(p), ts); } else { xml_unexpected_end_tag(p, name, "tileset"); } } static void XMLCALL tmx_tileset_el_start(void *pv, const char *name, const char **attr) { XML_Parser p = (XML_Parser) pv; char *dirname; char *source; char *path; struct image *img; debug("<%s> (tileset child)", name); if (xml_check_tag(name, "image")) { dirname = ((struct tileset *) xml_node_peek(p))->dirname; xml_get_string_attr(p, attr, "source", &source, 0); path = malloc(strlen(dirname) + strlen(source) + 2); if (path == NULL) { return; } sprintf(path, "%s/%s", dirname, source); img = img_png_get(path); free(source); xml_node_push(p, img, tmx_invalid_start, tmx_image_end, NULL); } else { xml_unexpected_start_tag(p, name, "image"); } } static void XMLCALL tmx_image_end(void *pv, const char *name) { XML_Parser p = (XML_Parser) pv; struct image *img; struct tileset *ts; debug(" (image)", name); if (xml_check_tag(name, "image")) { img = (struct image *) xml_node_pop(p); ts = (struct tileset *) xml_node_peek(p); ts->image = img; } else { xml_unexpected_end_tag(p, name, "image"); } } static void XMLCALL tmx_unused_start(void *pv, const char *name, const char **attr) { XML_Parser p = (XML_Parser) pv; debug("<%s> (unused)", name); /* Shut up, GCC. */ for (; *name != '\0'; ++name); for (; *attr != NULL; ++attr); xml_node_push(p, NULL, tmx_unused_start, tmx_unused_end, NULL); } static void XMLCALL tmx_unused_end(void *pv, const char *name) { XML_Parser p = (XML_Parser) pv; debug("<%s> (unused)", name); /* Shut up, GCC. */ for (; *name != '\0'; ++name); xml_node_pop(p); } static void XMLCALL tmx_invalid_start(void *pv, const char *name, const char **attr) { XML_Parser p = (XML_Parser) pv; debug("<%s> (invalid)", name); /* Shut up, GCC. */ for (; *attr != NULL; ++attr); xml_unexpected_start_tag(p, name, ""); } static void XMLCALL tmx_invalid_end(void *pv, const char *name) { XML_Parser p = (XML_Parser) pv; debug("<%s> (invalid)", name); xml_unexpected_end_tag(p, name, ""); }