#include #include #include #include #include "map.h" #include "resource.h" #include "tileset.h" #include "../xml.h" #include "../logging.h" struct tmx { XML_Parser p; struct map *map; char *layer_data; }; struct resource_table map_res; static void XMLCALL xml_map_start(void *data, const char *el, const char **attr); static void XMLCALL xml_map_end(void *data, const char *el); static void XMLCALL xml_mapelem_start(void *data, const char *el, const char **attr); static void XMLCALL xml_mapelem_end(void *data, const char *el); static void XMLCALL xml_image_start(void *data, const char *el, const char **attr); static void XMLCALL xml_image_end(void *data, const char *el); static void XMLCALL xml_data_start(void *data, const char *el, const char **attr); static void XMLCALL xml_data_data(void *data, const char *s, int len); static void XMLCALL xml_data_end(void *data, const char *el); static void XMLCALL xml_object_start(void *data, const char *el, const char **attr); static void XMLCALL xml_object_end(void *data, const char *el); void map_add_layer(struct map *m, struct layer *l); void map_add_tileset(struct map *m, struct tileset *t); static void XMLCALL xml_map_start(void *data, const char *el, const char **attr) { struct tmx *tmx; debug("<%s> (type \"map\")", el); tmx = (struct tmx *) data; if (xml_check_tag(el, "map")) { if (tmx->map != NULL) { warn("Found multiple maps in TMX file"); XML_StopParser(tmx->p, XML_FALSE); } tmx->map = malloc(sizeof(*tmx->map)); if (tmx->map == NULL) { warn("Failed to allocate map"); XML_StopParser(tmx->p, XML_FALSE); } memset(tmx->map, 0, sizeof(*tmx->map)); xml_get_int_attr(tmx->p, attr, "width", &tmx->map->width, 1); xml_get_int_attr(tmx->p, attr, "width", &tmx->map->width, 1); xml_get_int_attr(tmx->p, attr, "tilewidth", &tmx->map->tilewidth, 1); xml_get_int_attr(tmx->p, attr, "tileheight", &tmx->map->tileheight, 1); XML_SetStartElementHandler(tmx->p, xml_mapelem_start); XML_SetEndElementHandler(tmx->p, xml_map_end); } else { xml_unexpected_start_tag(tmx->p, el, "map"); } } static void XMLCALL xml_map_end(void *data, const char *el) { struct tmx *tmx; debug(" (type \"map\")", el); tmx = (struct tmx *) data; if (xml_check_tag(el, "map")) { XML_SetStartElementHandler(tmx->p, NULL); XML_SetEndElementHandler(tmx->p, NULL); } else { xml_unexpected_end_tag(tmx->p, el, "map"); } } static void XMLCALL xml_mapelem_start(void *data, const char *el, const char **attr) { struct tmx *tmx; char *source; struct tileset *ts; struct layer *ly; debug("<%s> (type \"mapelem\")", el); tmx = (struct tmx *) data; if (xml_check_tag(el, "tileset")) { source = NULL; xml_get_string_attr(tmx->p, attr, "source", &source, 0); if (source != NULL) { ts = tileset_get(source); free(source); XML_SetStartElementHandler(tmx->p, NULL); XML_SetEndElementHandler(tmx->p, xml_tileset_end); } else { ts = resource_alloc("", sizeof(*ts)); xml_get_int_attr(tmx->p, attr, "tilewidth", &ts->tilewidth, 1); xml_get_int_attr(tmx->p, attr, "tileheight", &ts->tileheight, 1); /* TODO: Change xml_image_start to xml_tselem_start. * A can contain: * * Zero or one , * * Zero or one s, and * * Zero or more s. */ XML_SetStartElementHandler(tmx->p, xml_image_start); XML_SetEndElementHandler(tmx->p, xml_tileset_end); } xml_get_int_attr(tmx->p, attr, "firstgid", &ts->firstgid, 1); xml_get_string_attr(tmx->p, attr, "name", &ts->name, 1); map_add_tileset(tmx->map, ts); } else if (xml_check_tag(el, "layer")) { ly = resource_alloc("", sizeof(*ly)); xml_get_string_attr(tmx->p, attr, "name", &ly->name, 1); map_add_layer(tmx->map, ly); XML_SetStartElementHandler(tmx->p, xml_data_start); XML_SetEndElementHandler(tmx->p, xml_mapelem_end); } else if (xml_check_tag(el, "objectgroup")) { XML_SetStartElementHandler(tmx->p, xml_object_start); XML_SetEndElementHandler(tmx->p, xml_mapelem_end); } else { xml_unexpected_start_tag(tmx->p, el, "tileset, layer, or objectgroup"); } } static void XMLCALL xml_mapelem_end(void *data, const char *el) { struct tmx *tmx; debug(" (type \"mapelem\")", el); tmx = (struct tmx *) data; if (xml_check_tag(el, "tileset")) { XML_SetStartElementHandler(tmx->p, xml_mapelem_start); XML_SetEndElementHandler(tmx->p, xml_map_end); } else if (xml_check_tag(el, "layer")) { XML_SetStartElementHandler(tmx->p, xml_mapelem_start); XML_SetEndElementHandler(tmx->p, xml_map_end); } else if (xml_check_tag(el, "objectgroup")) { XML_SetStartElementHandler(tmx->p, xml_mapelem_start); XML_SetEndElementHandler(tmx->p, xml_map_end); } else { xml_unexpected_end_tag(tmx->p, el, "tileset, layer, or objectgroup"); } } static void XMLCALL xml_image_start(void *data, const char *el, const char **attr) { struct tmx *tmx; struct tileset *ts; char *source; debug("<%s> (type \"image\")", el); tmx = (struct tmx *) data; if (xml_check_tag(el, "image")) { ts = tmx->map->tilesets_tail; xml_get_string_attr(tmx->p, attr, "source", &source, 1); ts->image = img_png_get(source); free(source); XML_SetStartElementHandler(tmx->p, NULL); XML_SetEndElementHandler(tmx->p, xml_image_end); } else { xml_unexpected_end_tag(tmx->p, el, "image"); } } static void XMLCALL xml_image_end(void *data, const char *el) { struct tmx *tmx; tmx = (struct tmx *) data; debug(" (type \"image\")", el); if (xml_check_tag(el, "image")) { XML_SetStartElementHandler(tmx->p, NULL); XML_SetEndElementHandler(tmx->p, xml_mapelem_end); } else { xml_unexpected_end_tag(tmx->p, el, "image"); } } static void XMLCALL xml_data_start(void *data, const char *el, const char **attr) { struct tmx *tmx; struct layer *ly; debug("<%s> (type \"data\")", el); tmx = (struct tmx *) data; if (xml_check_tag(el, "data")) { ly = tmx->map->layers_tail; xml_get_string_attr(tmx->p, attr, "encoding", &ly->encoding, 0); xml_get_string_attr(tmx->p, attr, "compression", &ly->compression, 0); XML_SetStartElementHandler(tmx->p, NULL); XML_SetEndElementHandler(tmx->p, xml_data_end); XML_SetCharacterDataHandler(tmx->p, xml_data_data); } else { xml_unexpected_start_tag(tmx->p, el, "data"); } } static void XMLCALL xml_data_data(void *data, const char *s, int len) { struct tmx *tmx; char *s_z; char *s_z_trimmed, *s_z_trimmed_end; debug("...LAYER DATA..."); tmx = (struct tmx *) data; s_z = s_z_trimmed = strndup(s, len); while(isspace(*s_z_trimmed)) { ++s_z_trimmed; --len; } if (*s_z_trimmed == '\0') { return; } s_z_trimmed_end = s_z_trimmed + len - 1; while (s_z_trimmed_end > s_z_trimmed && isspace(*s_z_trimmed_end)) { --s_z_trimmed_end; } *(s_z_trimmed_end + 1) = '\0'; tmx->layer_data = strdup(s_z_trimmed); free(s_z); } static void XMLCALL xml_data_end(void *data, const char *el) { struct tmx *tmx; debug(" (type \"data\")", el); tmx = (struct tmx *) data; if (xml_check_tag(el, "data")) { XML_SetStartElementHandler(tmx->p, NULL); XML_SetEndElementHandler(tmx->p, xml_mapelem_end); XML_SetCharacterDataHandler(tmx->p, NULL); } else { xml_unexpected_end_tag(tmx->p, el, "data"); } } static void XMLCALL xml_object_start(void *data, const char *el, const char **attr) { struct tmx *tmx; char *type; debug("<%s> (type \"object\")", el); tmx = (struct tmx *) data; if (xml_check_tag(el, "object")) { /* TODO: Handle objects. */ xml_get_string_attr(tmx->p, attr, "type", &type, 1); if (strcmp(type, "exit") == 0) { } else if (strcmp(type, "exit") == 0) { } XML_SetStartElementHandler(tmx->p, NULL); XML_SetEndElementHandler(tmx->p, xml_object_end); } else { xml_unexpected_start_tag(tmx->p, el, "object"); } } static void XMLCALL xml_object_end(void *data, const char *el) { struct tmx *tmx; debug(" (type \"object\")", el); tmx = (struct tmx *) data; if (xml_check_tag(el, "object")) { XML_SetStartElementHandler(tmx->p, xml_object_start); XML_SetEndElementHandler(tmx->p, xml_mapelem_end); } else { xml_unexpected_end_tag(tmx->p, el, "object"); } } struct map * map_get(const char *path) { struct tmx tmx; FILE *tmx_fp; void *tmx_buf; size_t len; enum XML_Status status; memset(&tmx, 0, sizeof(tmx)); tmx.map = (struct map *) resource_get(&map_res, path); if (tmx.map != NULL) { resource_use((struct resource *) tmx.map); return tmx.map; } tmx.map = resource_alloc(path, sizeof(*tmx.map)); tmx.p = XML_ParserCreate(NULL); if (tmx.p == NULL) { warn("Failed to create TMX parser"); return NULL; } XML_SetStartElementHandler(tmx.p, xml_map_start); XML_SetEndElementHandler(tmx.p, NULL); XML_SetCharacterDataHandler(tmx.p, NULL); XML_SetUserData(tmx.p, &tmx); tmx_fp = fopen(path, "rb"); if (tmx_fp == NULL) { warn("Failed to open TMX file"); XML_ParserFree(tmx.p); return NULL; } tmx_buf = XML_GetBuffer(tmx.p, 8192); if (tmx_buf == NULL) { warn("Failed to create TMX parse buffer"); XML_ParserFree(tmx.p); fclose(tmx_fp); return NULL; } while (!feof(tmx_fp)) { len = fread(tmx_buf, 1, 8192, tmx_fp); status = XML_ParseBuffer(tmx.p, len, feof(tmx_fp)); if (status == XML_STATUS_OK) { continue; } warn("Failed to parse TMX file (%s)", XML_ErrorString(XML_GetErrorCode(tmx.p))); XML_ParserFree(tmx.p); fclose(tmx_fp); return NULL; } XML_ParserFree(tmx.p); fclose(tmx_fp); resource_use((struct resource *) tmx.map); resource_add(&map_res, path, (struct resource *) tmx.map); return tmx.map; } void map_add_layer(struct map *m, struct layer *l) { if (m->layers_head == NULL) { m->layers_head = l; } else { m->layers_tail->next = l; } m->layers_tail = l; } void map_add_tileset(struct map *m, struct tileset *t) { if (m->tilesets_head == NULL) { m->tilesets_head = t; } else { m->tilesets_tail->next = t; } m->tilesets_tail = t; }