/*
* Copyright (C) 2013 Patrick "P. J." McDermott
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* This program 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this program. If not, see
* .
*/
#include
#include
#include "tileset.h"
#include "resource.h"
#include "tmx.h"
#include "image.h"
#include "../xml.h"
#include "../logging.h"
struct resource_table ts_res;
static void XMLCALL tmx_tileset_start(void *, const char *, const char **);
static void XMLCALL tmx_tileset_end(void *, const char *);
static void XMLCALL tmx_image_end(void *, const char *);
static void XMLCALL tmx_tile_end(void *, const char *);
static void XMLCALL tmx_tile_properties_start(void *, const char *,
const char **);
static void XMLCALL tmx_tile_property_start(void *, const char *,
const char **);
struct tileset *
tileset_get(const char *path, const char *basedir)
{
struct tileset *ts;
XML_Parser p;
FILE *tmx_fp;
void *tmx_buf;
size_t len;
enum XML_Status status;
ts = (struct tileset *) resource_get(&ts_res, path);
if (ts != NULL) {
resource_use((struct resource *) ts);
return ts;
}
ts = resource_alloc(path, sizeof(*ts));
if (ts == NULL) {
return NULL;
}
ts->dirname = strdup(basedir);
p = XML_ParserCreate(NULL);
if (p == NULL) {
warn("Failed to create TSX parser");
return NULL;
}
XML_UseParserAsHandlerArg(p);
xml_node_push(p, ts, tmx_tileset_start, tmx_invalid_end, NULL);
tmx_fp = fopen(path, "rb");
if (tmx_fp == NULL) {
warn("Failed to open TSX file");
xml_node_pop(p);
XML_ParserFree(p);
return NULL;
}
tmx_buf = XML_GetBuffer(p, 8192);
if (tmx_buf == NULL) {
warn("Failed to create TSX parse buffer");
xml_node_pop(p);
XML_ParserFree(p);
fclose(tmx_fp);
return NULL;
}
while (!feof(tmx_fp)) {
len = fread(tmx_buf, 1, 8192, tmx_fp);
status = XML_ParseBuffer(p, len, feof(tmx_fp));
if (status == XML_STATUS_OK) {
continue;
}
warn("Failed to parse TSX file (%s)",
XML_ErrorString(XML_GetErrorCode(p)));
xml_node_pop(p);
XML_ParserFree(p);
fclose(tmx_fp);
return NULL;
}
XML_ParserFree(p);
fclose(tmx_fp);
resource_use((struct resource *) ts);
resource_add(&ts_res, path, (struct resource *) ts);
return ts;
}
void XMLCALL
tmx_tileset_el_start(void *pv, const char *name, const char **attr)
{
XML_Parser p = (XML_Parser) pv;
struct tileset *ts;
char *dirname;
char *source;
char *path;
struct image *img;
#ifdef DEBUG_TMX
debug("<%s> (tileset child)", name);
#endif
ts = xml_node_peek(p);
if (xml_check_tag(name, "tileoffset")) {
/* Not used by engine. */
xml_node_push(p, NULL, tmx_unused_start, tmx_unused_end, NULL);
} else if (xml_check_tag(name, "properties")) {
/* Not used by engine. */
xml_node_push(p, NULL, tmx_unused_start, tmx_unused_end, NULL);
} else if (xml_check_tag(name, "image")) {
xml_get_int_attr(p, attr, "width", &ts->width, 1);
xml_get_int_attr(p, attr, "height", &ts->height, 1);
ts->width /= ts->tilewidth;
ts->height /= ts->tileheight;
img = NULL;
if (ts->type == TILESET_TYPE_IMAGE) {
dirname = ts->dirname;
xml_get_string_attr(p, attr, "source", &source, 0);
path = malloc(strlen(dirname) + strlen(source) + 2);
if (path == NULL) {
return;
}
sprintf(path, "%s/%s", dirname, source);
img = img_png_get(path);
/* TODO: Get the color key from the trans attribute. */
SDL_SetColorKey(img->image, SDL_SRCCOLORKEY,
SDL_MapRGB(img->image->format,
0xFC, 0x00, 0xFF));
free(source);
} else if (ts->type == TILESET_TYPE_COLLISION) {
ts->collision_tiles = malloc(ts->width * ts->height *
sizeof(*ts->collision_tiles));
if (ts->collision_tiles == NULL) {
return;
}
}
xml_node_push(p, img, tmx_invalid_start, tmx_image_end, NULL);
} else if (xml_check_tag(name, "tile")) {
xml_get_int_attr(p, attr, "id", &ts->cur_collision_tile, 1);
xml_node_push(p, ts, tmx_tile_properties_start, tmx_tile_end,
NULL);
} else {
xml_unexpected_start_tag(p, name, "tileoffset, image, tile");
}
}
static void XMLCALL
tmx_tileset_start(void *pv, const char *name, const char **attr)
{
XML_Parser p = (XML_Parser) pv;
struct tileset *ts;
#ifdef DEBUG_TMX
debug("<%s> (external tileset)", name);
#endif
if (xml_check_tag(name, "tileset")) {
ts = xml_node_peek(p);
xml_get_string_attr(p, attr, "name", &ts->name, 1);
xml_get_int_attr(p, attr, "tilewidth", &ts->tilewidth, 1);
xml_get_int_attr(p, attr, "tileheight", &ts->tileheight, 1);
if (strcmp(ts->name, "collision") == 0) {
ts->type = TILESET_TYPE_COLLISION;
} else {
ts->type = TILESET_TYPE_IMAGE;
}
xml_node_push(p, ts, tmx_tileset_el_start, tmx_tileset_end,
NULL);
} else {
xml_unexpected_start_tag(p, name, "tileset");
}
}
static void XMLCALL
tmx_tileset_end(void *pv, const char *name)
{
XML_Parser p = (XML_Parser) pv;
#ifdef DEBUG_TMX
debug("%s> (external tileset)", name);
#endif
if (xml_check_tag(name, "tileset")) {
xml_node_pop(p);
} else {
xml_unexpected_end_tag(p, name, "tileset");
}
}
static void XMLCALL
tmx_image_end(void *pv, const char *name)
{
XML_Parser p = (XML_Parser) pv;
struct image *img;
struct tileset *ts;
#ifdef DEBUG_TMX
debug("%s> (image)", name);
#endif
if (xml_check_tag(name, "image")) {
img = (struct image *) xml_node_pop(p);
ts = (struct tileset *) xml_node_peek(p);
ts->image = img;
} else {
xml_unexpected_end_tag(p, name, "image");
}
}
static void XMLCALL
tmx_tile_end(void *pv, const char *name)
{
XML_Parser p = (XML_Parser) pv;
#ifdef DEBUG_TMX
debug("%s> (tile)", name);
#endif
xml_node_pop(p);
if (xml_check_tag(name, "tile")) {
} else {
xml_unexpected_end_tag(p, name, "tile");
}
}
static void XMLCALL
tmx_tile_properties_start(void *pv, const char *name, const char **attr)
{
XML_Parser p = (XML_Parser) pv;
struct tileset *ts;
#ifdef DEBUG_TMX
debug("<%s> (tile properties)", name);
#endif
ts = xml_node_peek(p);
if (xml_check_tag(name, "properties")) {
/* has no attributes, but GCC warns of an
* "unused parameter ‘attr’". */
for (; 0; ++attr);
xml_node_push(p, ts, tmx_tile_property_start, tmx_unused_end,
NULL);
} else {
xml_unexpected_start_tag(p, name, "properties");
}
}
static void XMLCALL
tmx_tile_property_start(void *pv, const char *name, const char **attr)
{
XML_Parser p = (XML_Parser) pv;
struct tileset *ts;
char *attr_name;
char *attr_value;
int coll_t, coll_r, coll_b, coll_l;
int n;
#ifdef DEBUG_TMX
debug("<%s> (tile property)", name);
#endif
ts = xml_node_peek(p);
if (xml_check_tag(name, "property")) {
xml_get_string_attr(p, attr, "name", &attr_name, 1);
xml_get_string_attr(p, attr, "value", &attr_value, 1);
if (strcmp(attr_name, "collision") ==0) {
n = sscanf(attr_value, "%d,%d,%d,%d",
&coll_t, &coll_r, &coll_b, &coll_l);
if (n != 4) {
return;
}
ts->collision_tiles[ts->cur_collision_tile] =
coll_t << 3 | coll_r << 2 |
coll_b << 1 | coll_l << 0;
}
free(attr_name);
free(attr_value);
for (; 0; ++attr, ++ts);
xml_node_push(p, NULL, tmx_invalid_start, tmx_unused_end, NULL);
} else {
xml_unexpected_start_tag(p, name, "property");
}
}