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.c161
1 files changed, 161 insertions, 0 deletions
diff --git a/src/resources/tileset.c b/src/resources/tileset.c
new file mode 100644
index 0000000..e15224a
--- /dev/null
+++ b/src/resources/tileset.c
@@ -0,0 +1,161 @@
+#include <expat.h>
+#include "tileset.h"
+#include "../xml.h"
+#include "../logging.h"
+
+/*
+ * Problem:
+ * xml_tselem_start(), xml_tileset_end(), etc. may be called with one of two
+ * types of user data.
+ * Possible solution:
+ * 1. Create a base (struct xml_doc) in src/xml.h:
+ * struct xml_doc {
+ * enum {
+ * XML_DOC_TMX,
+ * XML_DOC_TSX
+ * } type;
+ * XML_Parser p;
+ * };
+ * 2. Make (struct tmx) and (struct tmx) "extend" it, e.g.:
+ * struct tsx {
+ * struct xml_doc doc;
+ * struct tileset *ts;
+ * };
+ * 3. Make xml_tselem_start(), xml_tileset_end(), etc. check the doc.type
+ * member of the user data and find the (struct tileset *) accordingly.
+ * Wait, what?!
+ * Alternative solution:
+ * 1. Declare (struct tmx) in src/resources/map.h.
+ * 2. Add to (struct tsx) the following member:
+ * struct tmx *parent_tmx;
+ * 3. In xml_mapelem_start() (src/resources/map.c):
+ * 1. Allocate a (struct tsx).
+ * 2. Set the parent_tmx member of the (struct tsx) to tmx.
+ * 3. Set the ts member of the (struct tsx) to ts.
+ * 4. Set the (struct tsx) as the user data.
+ * 4. In xml_tileset_end() (src/resources/tileset.c), add the following code:
+ * if (tsx->parent_tmx != NULL) {
+ * XML_SetUserData(tsx->p, tsx->parent_tmx);
+ * free(tsx);
+ * XML_SetStartElementHandler(tmx->p, xml_mapelem_start);
+ * XML_SetEndElementHandler(tmx->p, xml_map_end);
+ * }
+ * Alternative alternative solution:
+ * Something that doesn't suck.
+ */
+struct tsx {
+ XML_Parser p;
+ struct tileset *ts;
+};
+
+struct resource_table ts_res;
+
+void XMLCALL
+xml_tileset_start(void *data, const char *el, const char **attr)
+{
+ struct tsx *tsx;
+
+ debug("<%s> (type \"tileset\")", el);
+
+ tsx = (struct tsx *) data;
+
+ if (xml_check_tag(el, "tileset")) {
+ xml_get_string_attr(tsx->p, attr, "name",
+ &tsx->ts->name, 1);
+ xml_get_int_attr(tsx->p, attr, "tilewidth",
+ &tsx->ts->tilewidth, 1);
+ xml_get_int_attr(tsx->p, attr, "tileheight",
+ &tsx->ts->tileheight, 1);
+/* XML_SetStartElementHandler(tsx->p, xml_tselem_start);*/
+ XML_SetEndElementHandler(tsx->p, xml_tileset_end);
+ } else {
+ xml_unexpected_start_tag(tsx->p, el,
+ "tileset");
+ }
+}
+
+void XMLCALL
+xml_tileset_end(void *data, const char *el)
+{
+ struct tsx *tsx;
+
+ debug("</%s> (type \"tileset\")", el);
+
+ tsx = (struct tsx *) data;
+
+ if (xml_check_tag(el, "tileset")) {
+ XML_SetStartElementHandler(tsx->p, NULL);
+ XML_SetEndElementHandler(tsx->p, NULL);
+ } else {
+ xml_unexpected_end_tag(tsx->p, el,
+ "tileset");
+ }
+}
+
+struct tileset *
+tileset_get(const char *path)
+{
+ struct tsx tsx;
+ FILE *tsx_fp;
+ void *tsx_buf;
+ size_t len;
+ enum XML_Status status;
+
+ memset(&tsx, 0, sizeof(tsx));
+
+ tsx.ts = (struct tileset *) resource_get(&ts_res, path);
+ if (tsx.ts != NULL) {
+ resource_use((struct resource *) tsx.ts);
+ return tsx.ts;
+ }
+
+ tsx.ts = resource_alloc(path, sizeof(*tsx.ts));
+
+ tsx.p = XML_ParserCreate(NULL);
+ if (tsx.p == NULL) {
+ warn("Failed to create TSX parser");
+ return NULL;
+ }
+
+ XML_SetStartElementHandler(tsx.p, xml_tileset_start);
+ XML_SetEndElementHandler(tsx.p, NULL);
+ XML_SetCharacterDataHandler(tsx.p, NULL);
+
+ XML_SetUserData(tsx.p, &tsx);
+
+ tsx_fp = fopen(path, "rb");
+ if (tsx_fp == NULL) {
+ warn("Failed to open TSX file");
+ XML_ParserFree(tsx.p);
+ return NULL;
+ }
+
+ tsx_buf = XML_GetBuffer(tsx.p, 8192);
+ if (tsx_buf == NULL) {
+ warn("Failed to create TSX parse buffer");
+ XML_ParserFree(tsx.p);
+ fclose(tsx_fp);
+ return NULL;
+ }
+
+ while (!feof(tsx_fp)) {
+ len = fread(tsx_buf, 1, 8192, tsx_fp);
+ status = XML_ParseBuffer(tsx.p, len, feof(tsx_fp));
+ if (status == XML_STATUS_OK) {
+ continue;
+ }
+ warn("Failed to parse TSX file (%s)",
+ XML_ErrorString(XML_GetErrorCode(tsx.p)));
+ XML_ParserFree(tsx.p);
+ fclose(tsx_fp);
+ return NULL;
+ }
+
+ XML_ParserFree(tsx.p);
+ fclose(tsx_fp);
+
+ resource_use((struct resource *) tsx.ts);
+ resource_add(&ts_res, path, (struct resource *) tsx.ts);
+
+ return tsx.ts;
+}