diff options
-rw-r--r-- | src/local.mk | 2 | ||||
-rw-r--r-- | src/map.c | 48 | ||||
-rw-r--r-- | src/tileset.c | 412 | ||||
-rw-r--r-- | src/tileset.h | 28 |
4 files changed, 481 insertions, 9 deletions
diff --git a/src/local.mk b/src/local.mk index 3d12bb4..c796ae7 100644 --- a/src/local.mk +++ b/src/local.mk @@ -19,6 +19,8 @@ dodge_balls_SOURCES += \ %reldir%/map.h \ %reldir%/output.c \ %reldir%/output.h \ + %reldir%/tileset.c \ + %reldir%/tileset.h \ %reldir%/util.c \ %reldir%/util.h \ %reldir%/xml.c \ @@ -24,18 +24,22 @@ #include "defs.h" #include "dirs.h" #include "map.h" +#include "tileset.h" #include "output.h" #include "xml.h" struct db_map { - int w; - int h; - int tw; - int th; - Uint8 bg_r; - Uint8 bg_g; - Uint8 bg_b; - int fr; + 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; }; static char * @@ -207,6 +211,8 @@ _db_tmx_map_el_start(void *pv, const char *name, const char **attr) { XML_Parser p; struct db_map *map; + int firstgid; + char *source; db_dbg(" <%s> (map child)", name); @@ -217,7 +223,19 @@ _db_tmx_map_el_start(void *pv, const char *name, const char **attr) db_xml_node_push(p, map, _db_tmx_property_start, _db_tmx_properties_end, NULL); } else if (db_xml_check_tag(name, "tileset")) { - /* TODO: attr */ + db_xml_get_int_attr(p, attr, "firstgid", &firstgid, 1); + db_xml_get_string_attr(p, attr, "source", &source, 1); + db_dbg(" Tileset: <%s>", source); + map->tileset_tail = db_tileset_new(map->game_id, source, + firstgid, map->tileset_tail); + if (map->tileset_head == NULL) { + map->tileset_head = map->tileset_tail; + } + free(source); + if (map->tileset_tail == NULL) { + XML_StopParser(p, XML_FALSE); + return; + } db_xml_node_push(p, map, _db_tmx_invalid_start, _db_tmx_tileset_end, NULL); } else if (db_xml_check_tag(name, "layer")) { @@ -336,8 +354,16 @@ db_map_new(const char *game_id, const char *level_id) return NULL; } + map->game_id = strdup(game_id); + if (map->game_id == NULL) { + db_err("Failed to allocate memory"); + free(map); + return NULL; + } + p = XML_ParserCreate(NULL); if (p == NULL) { + free(map->game_id); free(map); return NULL; } @@ -349,6 +375,7 @@ db_map_new(const char *game_id, const char *level_id) db_xml_node_pop(p); XML_ParserFree(p); free(path); + free(map->game_id); free(map); return NULL; } @@ -358,6 +385,7 @@ db_map_new(const char *game_id, const char *level_id) db_xml_node_pop(p); XML_ParserFree(p); free(path); + free(map->game_id); free(map); return NULL; } @@ -368,6 +396,7 @@ db_map_new(const char *game_id, const char *level_id) XML_ParserFree(p); free(path); fclose(fp); + free(map->game_id); free(map); return NULL; } @@ -386,6 +415,7 @@ db_map_new(const char *game_id, const char *level_id) db_xml_node_pop(p); XML_ParserFree(p); fclose(fp); + free(map->game_id); free(map); return NULL; } diff --git a/src/tileset.c b/src/tileset.c new file mode 100644 index 0000000..382220d --- /dev/null +++ b/src/tileset.c @@ -0,0 +1,412 @@ +/* + * Copyright (C) 2021 P. J. McDermott + * + * This file is part of Dodge Balls + * + * Dodge Balls is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dodge Balls is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Dodge Balls. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <SDL.h> +#include <SDL_image.h> +#include <expat.h> +#include "dirs.h" +#include "output.h" +#include "tileset.h" +#include "xml.h" + +struct db_tileset { + char *game_id; + SDL_Surface *image; + int tw; + int th; + int tc; + int cols; + Uint32 b_col; + Uint32 p_col; + int cur_tile; + int firstgid; + struct db_tileset *next; +}; + +static char * +_db_tileset_path(const char *game_id, const char *file) +{ + const char *games_dir; + char *path; + + games_dir = db_get_games_dir(); + + path = malloc((strlen(games_dir) + strlen(game_id) + strlen(file) + 3) * + sizeof(*path)); + if (path == NULL) { + db_err("Failed to allocate memory"); + return NULL; + } + + sprintf(path, "%s/%s/%s", games_dir, game_id, file); + + return path; +} + +static void XMLCALL +_db_tsx_invalid_end(void *pv, const char *name) +{ + XML_Parser p; + + db_dbg(" </%s> (invalid)", name); + + p = (XML_Parser) pv; + + db_xml_node_pop(p); +} + +static void XMLCALL +_db_tsx_invalid_start(void *pv, const char *name, + const char **attr __attribute__((__unused__))) +{ + XML_Parser p; + + db_dbg(" <%s> (invalid)", name); + + p = (XML_Parser) pv; + + db_xml_node_push(p, NULL, _db_tsx_invalid_start, + _db_tsx_invalid_end, NULL); +} + +static void XMLCALL +_db_tsx_image_end(void *pv, const char *name) +{ + XML_Parser p; + + db_dbg(" </%s> (image)", name); + + p = (XML_Parser) pv; + + if (db_xml_check_tag(name, "image")) { + db_xml_node_pop(p); + } else { + db_xml_unexpected_end_tag(p, name, "image"); + } +} + +static void XMLCALL +_db_tsx_tile_end(void *pv, const char *name) +{ + XML_Parser p; + + db_dbg(" </%s> (tile)", name); + + p = (XML_Parser) pv; + + if (db_xml_check_tag(name, "tile")) { + db_xml_node_pop(p); + } else { + db_xml_unexpected_end_tag(p, name, "tile"); + } +} + +static void XMLCALL +_db_tsx_properties_end(void *pv, const char *name) +{ + XML_Parser p; + + db_dbg(" </%s> (properties)", name); + + p = (XML_Parser) pv; + + if (db_xml_check_tag(name, "properties")) { + db_xml_node_pop(p); + } else { + db_xml_unexpected_end_tag(p, name, "properties"); + } +} + +static void XMLCALL +_db_tsx_property_end(void *pv, const char *name) +{ + XML_Parser p; + + db_dbg(" </%s> (property)", name); + + p = (XML_Parser) pv; + + if (db_xml_check_tag(name, "property")) { + db_xml_node_pop(p); + } else { + db_xml_unexpected_end_tag(p, name, "property"); + } +} + +static void XMLCALL +_db_tsx_property_start(void *pv, const char *name, const char **attr) +{ + XML_Parser p; + struct db_tileset *tileset; + char *p_name; + char *p_type; + SDL_bool col; + + db_dbg(" <%s> (property)", name); + + p = (XML_Parser) pv; + tileset = db_xml_node_peek(p); + + if (db_xml_check_tag(name, "property")) { + db_xml_get_string_attr(p, attr, "name", &p_name, 1); + db_xml_get_string_attr(p, attr, "type", &p_type, 1); + if (strcmp(p_name, "ballcollides") == 0) { + if (strcmp(p_type, "bool") != 0) { + db_err("Ball collision must be a Boolean " + "value"); + free(p_name); + free(p_type); + XML_StopParser(p, XML_FALSE); + return; + } + col = SDL_FALSE; /* Shut up, GCC. */ + db_xml_get_bool_attr(p, attr, "value", &col, 1); + if (col) { + tileset->b_col &= (1 << tileset->cur_tile); + } + db_dbg(" Ball collision: %s", + (col ? "true" : "false")); + } else if (strcmp(p_name, "playercollides") == 0) { + if (strcmp(p_type, "bool") != 0) { + db_err("Player collision must be a Boolean " + "value"); + free(p_name); + free(p_type); + XML_StopParser(p, XML_FALSE); + return; + } + col = SDL_FALSE; /* Shut up, GCC. */ + db_xml_get_bool_attr(p, attr, "value", &col, 1); + if (col) { + tileset->p_col &= (1 << tileset->cur_tile); + } + db_dbg(" Player collision: %s", + (col ? "true" : "false")); + } else { + db_dbg(" Skipping unknown property \"%s\"", p_name); + } + free(p_name); + free(p_type); + db_xml_node_push(p, tileset, _db_tsx_invalid_start, + _db_tsx_property_end, NULL); + } else { + db_xml_unexpected_start_tag(p, name, "property"); + } +} + +static void XMLCALL +_db_tsx_properties_start(void *pv, const char *name, + const char **attr __attribute__((__unused__))) +{ + XML_Parser p; + struct db_tileset *tileset; + + db_dbg(" <%s> (properties)", name); + + p = (XML_Parser) pv; + tileset = db_xml_node_peek(p); + + if (db_xml_check_tag(name, "properties")) { + db_xml_node_push(p, tileset, _db_tsx_property_start, + _db_tsx_properties_end, NULL); + } else { + db_xml_unexpected_start_tag(p, name, "properties"); + } +} + +static void XMLCALL +_db_tsx_tileset_el_start(void *pv, const char *name, const char **attr) +{ + XML_Parser p; + struct db_tileset *tileset; + char *source; + char *path; + + db_dbg(" <%s> (tileset child)", name); + + p = (XML_Parser) pv; + tileset = db_xml_node_peek(p); + + if (db_xml_check_tag(name, "image")) { + db_xml_get_string_attr(p, attr, "source", &source, 1); + db_dbg(" Image: <%s>", source); + path = _db_tileset_path(tileset->game_id, source); + free(source); + if (path == NULL) { + XML_StopParser(p, XML_FALSE); + return; + } + tileset->image = IMG_Load(path); + free(path); + if (tileset->image == NULL) { + XML_StopParser(p, XML_FALSE); + return; + } + db_xml_node_push(p, tileset, _db_tsx_invalid_start, + _db_tsx_image_end, NULL); + } else if (db_xml_check_tag(name, "tile")) { + db_xml_get_int_attr(p, attr, "id", &tileset->cur_tile, 1); + db_dbg(" Tile size: %dx%d px", + tileset->tw, tileset->th); + db_xml_node_push(p, tileset, _db_tsx_properties_start, + _db_tsx_tile_end, NULL); + } else { + db_xml_unexpected_start_tag(p, name, "image or tile"); + } +} + +static void XMLCALL +_db_tsx_tileset_end(void *pv, const char *name) +{ + XML_Parser p; + + db_dbg(" </%s> (tileset)", name); + + p = (XML_Parser) pv; + + if (db_xml_check_tag(name, "tileset")) { + db_xml_node_pop(p); + } else { + db_xml_unexpected_end_tag(p, name, "tileset"); + } +} + +static void XMLCALL +_db_tsx_tileset_start(void *pv, const char *name, const char **attr) +{ + XML_Parser p; + struct db_tileset *tileset; + + db_dbg(" <%s> (tileset)", name); + + p = (XML_Parser) pv; + tileset = db_xml_node_peek(p); + + if (db_xml_check_tag(name, "tileset")) { + db_xml_get_int_attr(p, attr, "tilewidth", &tileset->tw, 1); + db_xml_get_int_attr(p, attr, "tileheight", &tileset->th, 1); + db_xml_get_int_attr(p, attr, "tilecount", &tileset->tc, 1); + db_xml_get_int_attr(p, attr, "columns", &tileset->cols, 1); + db_dbg(" Tile size: %dx%d px", + tileset->tw, tileset->th); + db_xml_node_push(p, tileset, _db_tsx_tileset_el_start, + _db_tsx_tileset_end, NULL); + } else { + db_xml_unexpected_start_tag(p, name, "tileset"); + } +} + +struct db_tileset * +db_tileset_new(const char *game_id, const char *file, int firstgid, + struct db_tileset *prev) +{ + struct db_tileset *tileset; + XML_Parser p; + char *path; + FILE *fp; + void *buf; + size_t len; + enum XML_Status status; + + tileset = calloc(1, sizeof(*tileset)); + if (tileset == NULL) { + db_err("Failed to allocate memory"); + return NULL; + } + + tileset->game_id = strdup(game_id); + if (tileset->game_id == NULL) { + db_err("Failed to allocate memory"); + free(tileset); + return NULL; + } + + p = XML_ParserCreate(NULL); + if (p == NULL) { + free(tileset->game_id); + free(tileset); + return NULL; + } + XML_UseParserAsHandlerArg(p); + db_xml_node_push(p, tileset, _db_tsx_tileset_start, _db_tsx_invalid_end, + NULL); + + path = _db_tileset_path(game_id, file); + if (path == NULL) { + db_xml_node_pop(p); + XML_ParserFree(p); + free(path); + free(tileset->game_id); + free(tileset); + return NULL; + } + + fp = fopen(path, "rb"); + if (fp == NULL) { + db_xml_node_pop(p); + XML_ParserFree(p); + free(path); + free(tileset->game_id); + free(tileset); + return NULL; + } + + buf = XML_GetBuffer(p, 8192); + if (buf == NULL) { + db_xml_node_pop(p); + XML_ParserFree(p); + free(path); + fclose(fp); + free(tileset->game_id); + free(tileset); + return NULL; + } + + db_dbg(" Parsing <%s>:", path); + free(path); + + while (!feof(fp)) { + len = fread(buf, 1, 8192, fp); + status = XML_ParseBuffer(p, len, feof(fp)); + if (status == XML_STATUS_OK) { + continue; + } + db_err("Failed to parse tileset information (%s)", + XML_ErrorString(XML_GetErrorCode(p))); + db_xml_node_pop(p); + XML_ParserFree(p); + fclose(fp); + free(tileset->game_id); + free(tileset); + return NULL; + } + + db_dbg(" Parsing done"); + + db_xml_node_pop(p); + XML_ParserFree(p); + fclose(fp); + + tileset->firstgid = firstgid; + if (prev != NULL) { + prev->next = tileset; + } + + return tileset; +} diff --git a/src/tileset.h b/src/tileset.h new file mode 100644 index 0000000..7a3a06a --- /dev/null +++ b/src/tileset.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2021 P. J. McDermott + * + * This file is part of Dodge Balls + * + * Dodge Balls is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * Dodge Balls is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Dodge Balls. If not, see <http://www.gnu.org/licenses/>. + */ + +#ifndef DB_TILESET_H_ +#define DB_TILESET_H_ + +struct db_tileset; + +struct db_tileset *db_tileset_new(const char *game_id, const char *file, + int firstgid, struct db_tileset *prev); + +#endif /* DB_TILESET_H_ */ |