summaryrefslogtreecommitdiffstats
path: root/src/resources/tileset.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/resources/tileset.c')
-rw-r--r--src/resources/tileset.c308
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");
+ }
+}