/* * 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 * . */ #include #include #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(" (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(" (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(" (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")) { /* 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"); } }