diff options
Diffstat (limited to 'src/resources/tileset.c')
-rw-r--r-- | src/resources/tileset.c | 308 |
1 files changed, 308 insertions, 0 deletions
diff --git a/src/resources/tileset.c b/src/resources/tileset.c new file mode 100644 index 0000000..05f055c --- /dev/null +++ b/src/resources/tileset.c @@ -0,0 +1,308 @@ +/* + * Copyright (C) 2013 Patrick "P. J." McDermott + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this program. If not, see + * <http://www.gnu.org/licenses/>. + */ + +#include <expat.h> +#include <config.h> +#include "tileset.h" +#include "resource.h" +#include "tmx.h" +#include "image.h" +#include "../xml.h" +#include "../logging.h" + +struct resource_table ts_res; + +static void XMLCALL tmx_tileset_start(void *, const char *, const char **); +static void XMLCALL tmx_tileset_end(void *, 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 **); + +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; +} + +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; + +#ifdef DEBUG_TMX + debug("<%s> (tileset child)", name); +#endif + + 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); + /* TODO: Get the color key from the trans attribute. */ + SDL_SetColorKey(img->image, SDL_SRCCOLORKEY, + SDL_MapRGB(img->image->format, + 0xFC, 0x00, 0xFF)); + 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_tileset_start(void *pv, const char *name, const char **attr) +{ + XML_Parser p = (XML_Parser) pv; + struct tileset *ts; + +#ifdef DEBUG_TMX + debug("<%s> (external tileset)", name); +#endif + + 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; + +#ifdef DEBUG_TMX + debug("</%s> (external tileset)", name); +#endif + + if (xml_check_tag(name, "tileset")) { + xml_node_pop(p); + } else { + xml_unexpected_end_tag(p, name, "tileset"); + } +} + +static void XMLCALL +tmx_image_end(void *pv, const char *name) +{ + XML_Parser p = (XML_Parser) pv; + struct image *img; + struct tileset *ts; + +#ifdef DEBUG_TMX + debug("</%s> (image)", name); +#endif + + 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; + +#ifdef DEBUG_TMX + debug("</%s> (tile)", name); +#endif + + 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; + +#ifdef DEBUG_TMX + debug("<%s> (tile properties)", name); +#endif + + ts = xml_node_peek(p); + + if (xml_check_tag(name, "properties")) { + /* <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; + +#ifdef DEBUG_TMX + debug("<%s> (tile property)", name); +#endif + + 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"); + } +} |