#include #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(" (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; }