From 6a7786f86faf22b7e0a2c844126ec99d7c622d24 Mon Sep 17 00:00:00 2001 From: P. J. McDermott Date: Tue, 23 Mar 2021 16:49:20 -0400 Subject: map: Parse layer data --- (limited to 'src/map.c') diff --git a/src/map.c b/src/map.c index 3e7c0e3..314ae92 100644 --- a/src/map.c +++ b/src/map.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2021 P. J. McDermott + * Copyright (C) 2013, 2021 P. J. McDermott * * This file is part of Dodge Balls * @@ -21,6 +21,8 @@ #include #include #include +#include "base64.h" +#include "compression.h" #include "defs.h" #include "dirs.h" #include "map.h" @@ -28,18 +30,28 @@ #include "output.h" #include "xml.h" +struct db_map_layer { + Uint32 *tiles; + char *encoding; + char *compression; + char *raw_data; + struct db_map_layer *next; +}; + struct db_map { - char *game_id; - int w; - int h; - int tw; - int th; - Uint8 bg_r; - Uint8 bg_g; - Uint8 bg_b; - int fr; - struct db_tileset *tileset_head; - struct db_tileset *tileset_tail; + char *game_id; + int w; + int h; + int tw; + int th; + Uint8 bg_r; + Uint8 bg_g; + Uint8 bg_b; + int fr; + struct db_tileset *tileset_head; + struct db_tileset *tileset_tail; + struct db_map_layer *layer_head; + struct db_map_layer *layer_tail; }; static char * @@ -175,6 +187,162 @@ _db_tmx_tileset_end(void *pv, const char *name) } static void XMLCALL +_db_tmx_cdata(void *pv, const char *s, int len) +{ + XML_Parser p; + struct db_map *map; + char *s_z; + char *s_z_trimmed; + char *s_z_trimmed_end; + char *old_data; + + p = (XML_Parser) pv; + map = db_xml_node_peek(p); + + s_z = s_z_trimmed = strndup(s, len); + + while(isspace(*s_z_trimmed)) { + ++s_z_trimmed; + --len; + } + if (*s_z_trimmed == '\0') { + db_dbg(" (null)"); + free(s_z); + 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'; + + db_dbg(" %s", s_z_trimmed); + if (map->layer_tail->raw_data != NULL && + *map->layer_tail->raw_data != '\0') { + old_data = map->layer_tail->raw_data; + sprintf(map->layer_tail->raw_data, "%s %s", + map->layer_tail->raw_data, strdup(s_z_trimmed)); + free(old_data); + } else { + map->layer_tail->raw_data = strdup(s_z_trimmed); + } + + free(s_z); +} + +static void XMLCALL +_db_tmx_data_end(void *pv, const char *name) +{ + XML_Parser p; + struct db_map *map; + size_t decoded_len; + size_t decomp_len; + char *decoded_buf; + char *decomp_buf; + size_t i; + + db_dbg(" (data)", name); + + p = (XML_Parser) pv; + map = db_xml_node_peek(p); + + if (db_xml_check_tag(name, "data")) { + decoded_len = strlen(map->layer_tail->raw_data); + decomp_len = 4 * map->w* map->h; + db_dbg(" Expected map data size: %zu", decomp_len); + decoded_buf = malloc(decoded_len + 1); + if (decoded_buf == NULL) { + db_warn(" Failed to allocate layer data buffer"); + XML_StopParser(p, XML_FALSE); + return; + } + if (strcmp(map->layer_tail->encoding, "base64") == 0) { + db_dbg(" Decoding base 64 layer data..."); + db_base64_decode(map->layer_tail->raw_data, + decoded_buf, decoded_len + 1); + } + if (map->layer_tail->compression == NULL) { + db_dbg(" Layer data already decompressed"); + decomp_buf = decoded_buf; + } else if (strcmp(map->layer_tail->compression, "zlib") == 0) { + db_dbg(" Decompressing layer data with zlib..."); + decomp_buf = malloc(decomp_len); + if (decomp_buf == NULL) { + db_err("Failed to allocate layer data buffer"); + free(decoded_buf); + XML_StopParser(p, XML_FALSE); + return; + } + db_decompress(decoded_buf, decoded_len, + decomp_buf, decomp_len); + free(decoded_buf); + } else if (strcmp(map->layer_tail->compression, "gzip") == 0) { + db_dbg(" Decompressing layer data with gzip..."); + decomp_buf = malloc(decomp_len); + if (decomp_buf == NULL) { + db_err("Failed to allocate layer data buffer"); + free(decoded_buf); + XML_StopParser(p, XML_FALSE); + return; + } + db_decompress(decoded_buf, decoded_len, + decomp_buf, decomp_len); + free(decoded_buf); + } else { + /* This should never happen. This branch exists only to + * silence GCC's maybe-uninitialized warning on + * decomp_buf below. */ + return; + } + free(map->layer_tail->raw_data); + map->layer_tail->tiles = malloc(decomp_len); + for (i = 0; i < decomp_len / 4; ++i) { + /* Convert each tile GID to the system's byte order. */ + map->layer_tail->tiles[i] = + (decomp_buf[i * 4 + 0] & 0xFF << 000) | + (decomp_buf[i * 4 + 1] & 0xFF << 010) | + (decomp_buf[i * 4 + 2] & 0xFF << 020) | + (decomp_buf[i * 4 + 3] & 0xFF << 030); + } + free(decomp_buf); + db_xml_node_pop(p); + } else { + db_xml_unexpected_end_tag(p, name, "data"); + } +} + +static void XMLCALL +_db_tmx_data_start(void *pv, const char *name, const char **attr) +{ + XML_Parser p; + struct db_map *map; + + db_dbg(" <%s> (data)", name); + + p = (XML_Parser) pv; + map = db_xml_node_peek(p); + + if (db_xml_check_tag(name, "data")) { + map->layer_tail = calloc(1, sizeof(*map->layer_tail)); + if (map->layer_tail == NULL) { + XML_StopParser(p, XML_FALSE); + return; + } + if (map->layer_head == NULL) { + map->layer_head = map->layer_tail; + } + db_xml_get_string_attr(p, attr, "encoding", + &map->layer_tail->encoding, 1); + db_xml_get_string_attr(p, attr, "compression", + &map->layer_tail->compression, 0); + db_xml_node_push(p, map, _db_tmx_invalid_start, + _db_tmx_data_end, _db_tmx_cdata); + } else { + db_xml_unexpected_start_tag(p, name, "data"); + } +} + +static void XMLCALL _db_tmx_layer_end(void *pv, const char *name) { XML_Parser p; @@ -239,8 +407,7 @@ _db_tmx_map_el_start(void *pv, const char *name, const char **attr) db_xml_node_push(p, map, _db_tmx_invalid_start, _db_tmx_tileset_end, NULL); } else if (db_xml_check_tag(name, "layer")) { - /* TODO: data */ - db_xml_node_push(p, map, _db_tmx_invalid_start, + db_xml_node_push(p, map, _db_tmx_data_start, _db_tmx_layer_end, NULL); } else if (db_xml_check_tag(name, "objectgroup")) { /* TODO: object */ -- cgit v0.9.1