diff options
Diffstat (limited to 'src/resources/tileset.c')
-rw-r--r-- | src/resources/tileset.c | 161 |
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; +} |