#include #include #include #include #include #include "map.h" #include "resource.h" #include "../xml.h" #include "../base64.h" #include "../compression.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_tilesetemb_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_tile_end(void *, const char *); static void XMLCALL tmx_tile_properties_start(void *, const char *, const char **); static void XMLCALL tmx_tile_property_start(void *, const char *, const char **); static void XMLCALL tmx_layer_el_start(void *, const char *, const char **); static void XMLCALL tmx_layer_end(void *, const char *); static void XMLCALL tmx_data_end(void *, const char *); static void XMLCALL tmx_data_cdata(void *, const char *, int); static void XMLCALL tmx_objectgroup_el_start(void *, const char *, const char **); static void XMLCALL tmx_objectgroup_end(void *, const char *); static void XMLCALL tmx_object_exit_el_start(void *, const char *, const char **); static void XMLCALL tmx_object_exit_end(void *, const char *); static void XMLCALL tmx_object_spawn_el_start(void *, const char *, const char **); static void XMLCALL tmx_object_spawn_end(void *, const char *); static void XMLCALL tmx_object_exit_property_start(void *, const char *, 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_tileset(struct map *m, struct tileset *t, int firstgid) { struct map_tileset *mts; mts = malloc(sizeof(*mts)); if (mts == NULL) { return; } mts->tileset = t; mts->firstgid = firstgid; mts->next = NULL; if (m->tilesets_head == NULL) { m->tilesets_head = mts; } else { m->tilesets_tail->next = mts; } m->tilesets_tail = mts; } struct layer * map_get_layer(struct map *m, const char *name) { if (strcmp(name, "ground") == 0) { return &m->layers[LAYER_GROUND]; } else if (strcmp(name, "obj-low") == 0) { return &m->layers[LAYER_OBJ_LOW]; } else if (strcmp(name, "char-bot") == 0) { return &m->layers[LAYER_CHAR_BOT]; } else if (strcmp(name, "obj-mid") == 0) { return &m->layers[LAYER_OBJ_MID]; } else if (strcmp(name, "char-top") == 0) { return &m->layers[LAYER_CHAR_TOP]; } else if (strcmp(name, "obj-high") == 0) { return &m->layers[LAYER_OBJ_HIGH]; } else if (strcmp(name, "collision") == 0) { return &m->layers[LAYER_COLLISION]; } else if (strcmp(name, "weather") == 0) { return &m->layers[LAYER_WEATHER]; } else { return NULL; } } void map_add_exit(struct map *m, struct map_exit *e) { int i, j; e->next = NULL; if (m->map_exits_head == NULL) { m->map_exits_head = e; } else { m->map_exits_tail->next = e; } m->map_exits_tail = e; for (i = 0; i < e->width; ++i) { for (j = 0; j < e->height; ++j) { m->collisions[j * m->width + i].type = COLLISION_EXIT; m->collisions[j * m->width + i].data.e = e; } } } 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); m->collisions = malloc(m->width * m->height * sizeof(*m->collisions)); if (m->collisions == NULL) { return; } memset(m->collisions, 0, m->width * m->height * sizeof(*m->collisions)); 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; struct map *m; char *dirname; char *source; char *path; struct tileset *ts; char *ly_name; struct layer *ly; debug("<%s> (map child)", name); m = xml_node_peek(p); 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 = m->dirname; source = NULL; xml_get_string_attr(p, attr, "source", &source, 0); if (source != NULL) { /* External tileset. */ 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_tilesetemb_end, NULL); } else { /* Embedded tileset. */ 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); if (strcmp(ts->name, "collision") == 0) { ts->type = TILESET_TYPE_COLLISION; } else { ts->type = TILESET_TYPE_IMAGE; } xml_node_push(p, ts, tmx_tileset_el_start, tmx_tilesetemb_end, NULL); } xml_get_int_attr(p, attr, "firstgid", &m->cur_ts_firstgid, 1); } else if (xml_check_tag(name, "layer")) { ly_name = NULL; xml_get_string_attr(p, attr, "name", &ly_name, 1); ly = map_get_layer(m, ly_name); free(ly_name); if (ly != NULL) { ly->map = m; xml_node_push(p, ly, tmx_layer_el_start, tmx_layer_end, NULL); } else { xml_node_push(p, NULL, tmx_unused_start, tmx_layer_end, NULL); } } else if (xml_check_tag(name, "objectgroup")) { xml_node_push(p, m, tmx_objectgroup_el_start, tmx_objectgroup_end, NULL); } 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> (external 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); if (strcmp(ts->name, "collision") == 0) { ts->type = TILESET_TYPE_COLLISION; } else { ts->type = TILESET_TYPE_IMAGE; } 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_tilesetemb_end(void *pv, const char *name) { XML_Parser p = (XML_Parser) pv; struct tileset *ts; struct map *m; debug(" (tileset)", name); if (xml_check_tag(name, "tileset")) { ts = xml_node_pop(p); m = xml_node_peek(p); map_add_tileset(m, ts, m->cur_ts_firstgid); } 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; struct tileset *ts; char *dirname; char *source; char *path; struct image *img; debug("<%s> (tileset child)", name); ts = xml_node_peek(p); if (xml_check_tag(name, "tileoffset")) { /* Not used by engine. */ xml_node_push(p, NULL, tmx_unused_start, tmx_unused_end, NULL); } else 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, "image")) { xml_get_int_attr(p, attr, "width", &ts->width, 1); xml_get_int_attr(p, attr, "height", &ts->height, 1); ts->width /= ts->tilewidth; ts->height /= ts->tileheight; img = NULL; if (ts->type == TILESET_TYPE_IMAGE) { dirname = ts->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); } else if (ts->type == TILESET_TYPE_COLLISION) { ts->collision_tiles = malloc(ts->width * ts->height * sizeof(*ts->collision_tiles)); if (ts->collision_tiles == NULL) { return; } } xml_node_push(p, img, tmx_invalid_start, tmx_image_end, NULL); } else if (xml_check_tag(name, "tile")) { xml_get_int_attr(p, attr, "id", &ts->cur_collision_tile, 1); xml_node_push(p, ts, tmx_tile_properties_start, tmx_tile_end, NULL); } else { xml_unexpected_start_tag(p, name, "tileoffset, image, tile"); } } 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_tile_end(void *pv, const char *name) { XML_Parser p = (XML_Parser) pv; debug(" (tile)", name); xml_node_pop(p); if (xml_check_tag(name, "tile")) { } else { xml_unexpected_end_tag(p, name, "tile"); } } static void XMLCALL tmx_tile_properties_start(void *pv, const char *name, const char **attr) { XML_Parser p = (XML_Parser) pv; struct tileset *ts; debug("<%s> (tile properties)", name); ts = xml_node_peek(p); if (xml_check_tag(name, "properties")) { /* has no attributes, but GCC warns of an * "unused parameter ‘attr’". */ for (; 0; ++attr); xml_node_push(p, ts, tmx_tile_property_start, tmx_unused_end, NULL); } else { xml_unexpected_start_tag(p, name, "properties"); } } static void XMLCALL tmx_tile_property_start(void *pv, const char *name, const char **attr) { XML_Parser p = (XML_Parser) pv; struct tileset *ts; char *attr_name; char *attr_value; int coll_t, coll_r, coll_b, coll_l; int n; debug("<%s> (tile property)", name); ts = xml_node_peek(p); if (xml_check_tag(name, "property")) { xml_get_string_attr(p, attr, "name", &attr_name, 1); xml_get_string_attr(p, attr, "value", &attr_value, 1); if (strcmp(attr_name, "collision") ==0) { n = sscanf(attr_value, "%d,%d,%d,%d", &coll_t, &coll_r, &coll_b, &coll_l); if (n != 4) { return; } ts->collision_tiles[ts->cur_collision_tile] = coll_t << 3 | coll_r << 2 | coll_b << 1 | coll_l << 0; } free(attr_name); free(attr_value); for (; 0; ++attr, ++ts); xml_node_push(p, NULL, tmx_invalid_start, tmx_unused_end, NULL); } else { xml_unexpected_start_tag(p, name, "property"); } } static void XMLCALL tmx_layer_el_start(void *pv, const char *name, const char **attr) { XML_Parser p = (XML_Parser) pv; struct layer *ly; debug("<%s> (layer child)", name); ly = xml_node_peek(p); 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, "data")) { /* NB: This parser requires an encoding. The author is too lazy * to parse a bunch of elements. */ xml_get_string_attr(p, attr, "encoding", &ly->encoding, 1); xml_get_string_attr(p, attr, "compression", &ly->compression, 0); xml_node_push(p, ly, tmx_invalid_start, tmx_data_end, tmx_data_cdata); } else { xml_unexpected_start_tag(p, name, "properties or data"); } } static void XMLCALL tmx_layer_end(void *pv, const char *name) { XML_Parser p = (XML_Parser) pv; debug(" (layer)", name); if (xml_check_tag(name, "layer")) { xml_node_pop(p); } else { xml_unexpected_end_tag(p, name, "layer"); } } static void XMLCALL tmx_data_end(void *pv, const char *name) { XML_Parser p = (XML_Parser) pv; struct layer *ly; size_t decoded_len; size_t decomp_len; char *decoded_buf; char *decomp_buf; debug(" (data)", name); ly = xml_node_peek(p); if (xml_check_tag(name, "data")) { decoded_len = strlen(ly->raw_data); decomp_len = 4 * ly->map->width * ly->map->height; debug("Expected map data size: %d", decomp_len); decoded_buf = malloc(decoded_len + 1); if (decoded_buf == NULL) { warn("Failed to allocate layer data buffer"); return; } if (strcmp(ly->encoding, "base64") == 0) { debug("Decoding base 64 layer data..."); base64_decode(ly->raw_data, decoded_buf, decoded_len + 1); } if (ly->compression == NULL) { debug("Layer data already decompressed"); decomp_buf = decoded_buf; } else if (strcmp(ly->compression, "zlib") == 0) { debug("Decompressing layer data with zlib..."); decomp_buf = malloc(decomp_len); if (decomp_buf == NULL) { warn("Failed to allocate layer data buffer"); return; } decompress(decoded_buf, decoded_len, decomp_buf, decomp_len); free(decoded_buf); } else if (strcmp(ly->compression, "gzip") == 0) { debug("Decompressing layer data with gzip..."); decomp_buf = malloc(decomp_len); if (decomp_buf == NULL) { warn("Failed to allocate layer data buffer"); } decompress(decoded_buf, decoded_len, decomp_buf, decomp_len); free(decoded_buf); } else { /* This should never happen. This branch exists only to * silence GCC's maybe-uninitialized warning on * decomp_buf below. */ return; } free(ly->raw_data); ly->tiles = (Uint32 *) decomp_buf; xml_node_pop(p); } else { xml_unexpected_end_tag(p, name, "data"); } } static void XMLCALL tmx_data_cdata(void *pv, const char *s, int len) { XML_Parser p = (XML_Parser) pv; struct layer *ly; char *s_z; char *s_z_trimmed, *s_z_trimmed_end; debug("[CDATA]"); ly = xml_node_peek(p); s_z = s_z_trimmed = strndup(s, len); while(isspace(*s_z_trimmed)) { ++s_z_trimmed; --len; } if (*s_z_trimmed == '\0') { return; } s_z_trimmed_end = s_z_trimmed + len - 1; while (s_z_trimmed_end > s_z_trimmed && isspace(*s_z_trimmed_end)) { --s_z_trimmed_end; } *(s_z_trimmed_end + 1) = '\0'; ly->raw_data = strdup(s_z_trimmed); free(s_z); } static void XMLCALL tmx_objectgroup_el_start(void *pv, const char * name, const char **attr) { XML_Parser p = (XML_Parser) pv; struct map *m; char *type; struct map_exit *e; struct map_spawn *s; debug("<%s> (objectgroup child)", name); m = xml_node_peek(p); 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, "object")) { type = NULL; xml_get_string_attr(p, attr, "type", &type, 0); if (type == NULL) { xml_node_push(p, NULL, tmx_unused_start, tmx_unused_end, NULL); } else if (strcmp(type, "exit") == 0) { e = malloc(sizeof(*e)); if (e == NULL) { return; } e->map = m; xml_get_int_attr(p, attr, "x", &e->x, 1); xml_get_int_attr(p, attr, "y", &e->y, 1); xml_get_int_attr(p, attr, "width", &e->width, 1); xml_get_int_attr(p, attr, "height", &e->height, 1); e->x /= m->tilewidth; e->y /= m->tileheight; e->width /= m->tilewidth; e->height /= m->tileheight; xml_node_push(p, e, tmx_object_exit_el_start, tmx_object_exit_end, NULL); } else if (strcmp(type, "spawn") == 0) { s = malloc(sizeof(*s)); if (s == NULL) { return; } s->map = m; xml_get_int_attr(p, attr, "x", &s->x, 1); xml_get_int_attr(p, attr, "y", &s->y, 1); xml_get_int_attr(p, attr, "width", &s->width, 1); xml_get_int_attr(p, attr, "height", &s->height, 1); xml_node_push(p, s, tmx_object_spawn_el_start, tmx_object_spawn_end, NULL); } else { xml_node_push(p, NULL, tmx_unused_start, tmx_unused_end, NULL); } } else { xml_unexpected_start_tag(p, name, "properties or object"); } } static void XMLCALL tmx_objectgroup_end(void *pv, const char *name) { XML_Parser p = (XML_Parser) pv; debug(" (objectgroup)", name); if (xml_check_tag(name, "objectgroup")) { xml_node_pop(p); } else { xml_unexpected_end_tag(p, name, "objectgroup"); } } static void XMLCALL tmx_object_exit_el_start(void *pv, const char * name, const char **attr) { XML_Parser p = (XML_Parser) pv; struct map_exit *e; debug("<%s> (object type=\"exit\" child)", name); e = xml_node_peek(p); if (xml_check_tag(name, "properties")) { /* has no attributes, but GCC warns of an * "unused parameter ‘attr’". */ for (; 0; ++attr); xml_node_push(p, e, tmx_object_exit_property_start, tmx_unused_end, NULL); } else if (xml_check_tag(name, "polygon")) { /* Not used by engine. */ xml_node_push(p, NULL, tmx_unused_start, tmx_unused_end, NULL); } else if (xml_check_tag(name, "polyline")) { /* Not used by engine. */ xml_node_push(p, NULL, tmx_unused_start, tmx_unused_end, NULL); } else { xml_unexpected_start_tag(p, name, "properties, polygon, or polyline"); } } static void XMLCALL tmx_object_exit_end(void *pv, const char *name) { XML_Parser p = (XML_Parser) pv; struct map_exit *e; struct map *m; debug(" (object type=\"exit\")", name); if (xml_check_tag(name, "object")) { e = xml_node_pop(p); m = xml_node_peek(p); map_add_exit(m, e); } else { xml_unexpected_end_tag(p, name, "object"); } } static void XMLCALL tmx_object_exit_property_start(void *pv, const char *name, const char **attr) { XML_Parser p = (XML_Parser) pv; struct map_exit *e; char *attr_name; char *attr_value; char *path; debug("<%s> (object type=\"exit\" property)", name); e = xml_node_peek(p); if (xml_check_tag(name, "property")) { xml_get_string_attr(p, attr, "name", &attr_name, 1); xml_get_string_attr(p, attr, "value", &attr_value, 1); if (strcmp(attr_name, "map") == 0) { e->target_map_name = strdup(attr_value); /* TODO: Lazily get map. We don't need to load the * entire world immediately. */ path = malloc(strlen(e->map->dirname) + strlen(attr_value) + 6); if (path == NULL) { return; } sprintf(path, "%s/%s.tmx", e->map->dirname, attr_value); e->target_map = map_get(path); free(path); /* Yeah, don't do that yet. ^ */ } else if (strcmp(attr_name, "coords") == 0) { sscanf(attr_value, "%d,%d", &e->target_x_coord, &e->target_y_coord); } free(attr_name); free(attr_value); xml_node_push(p, NULL, tmx_invalid_start, tmx_unused_end, NULL); } else { xml_unexpected_start_tag(p, name, "property"); } } static void XMLCALL tmx_object_spawn_el_start(void *pv, const char * name, const char **attr) { XML_Parser p = (XML_Parser) pv; struct map_spawn *s; debug("<%s> (object type=\"spawn\" child)", name); s = xml_node_peek(p); if (xml_check_tag(name, "properties")) { /* has no attributes, but GCC warns of an * "unused parameter ‘attr’". */ for (; 0; ++attr); /* TODO */ xml_node_push(p, s, tmx_unused_start, tmx_unused_end, NULL); } else if (xml_check_tag(name, "polygon")) { /* Not used by engine. */ xml_node_push(p, NULL, tmx_unused_start, tmx_unused_end, NULL); } else if (xml_check_tag(name, "polyline")) { /* Not used by engine. */ xml_node_push(p, NULL, tmx_unused_start, tmx_unused_end, NULL); } else { xml_unexpected_start_tag(p, name, "properties, polygon, or polyline"); } } static void XMLCALL tmx_object_spawn_end(void *pv, const char *name) { XML_Parser p = (XML_Parser) pv; debug(" (object type=\"spawn\")", name); if (xml_check_tag(name, "object")) { xml_node_pop(p); } else { xml_unexpected_end_tag(p, name, "object"); } } 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(" (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, ""); }