diff options
author | P. J. McDermott <pjm@nac.net> | 2013-02-16 21:35:48 (EST) |
---|---|---|
committer | P. J. McDermott <pjm@nac.net> | 2013-02-16 21:35:48 (EST) |
commit | 1e4407da677dd7a192c817de16583bb7199ceeb0 (patch) | |
tree | b97d3e7e746ef91ffebb86cb654e6fc8499241e5 /src/resources/map.c | |
parent | 63349a6230304a7f1aa34a4c96e4873f86bf39c5 (diff) | |
download | overworld-rpg-1e4407da677dd7a192c817de16583bb7199ceeb0.zip overworld-rpg-1e4407da677dd7a192c817de16583bb7199ceeb0.tar.gz overworld-rpg-1e4407da677dd7a192c817de16583bb7199ceeb0.tar.bz2 |
TMX parser rewrite number one.
Diffstat (limited to 'src/resources/map.c')
-rw-r--r-- | src/resources/map.c | 423 |
1 files changed, 423 insertions, 0 deletions
diff --git a/src/resources/map.c b/src/resources/map.c new file mode 100644 index 0000000..b703629 --- /dev/null +++ b/src/resources/map.c @@ -0,0 +1,423 @@ +#include <stdio.h> +#include <string.h> +#include <ctype.h> +#include <expat.h> +#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("</%s> (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 <tileset> can contain: + * * Zero or one <properties>, + * * Zero or one <image>s, and + * * Zero or more <tile>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("</%s> (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("</%s> (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("</%s> (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("</%s> (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; +} |