123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491 |
- #include <stdio.h>
- #include <stdarg.h>
- #include "tile.h"
- #include "tilemap.h"
- #include "tileio.h"
- #include "silut.h"
- #include "fifi.h"
- #include "monolog.h"
- #include "bxml.h"
- #include "bxmlparser.h"
- #define TILEMAP_MAX_WIDTH 1024
- #define TILEMAP_MAX_HEIGHT 1024
- /* Tile flipping. */
- #define TMX_FLIPPED_HORIZONTALLY 0x80000000
- #define TMX_FLIPPED_VERTICALLY 0x40000000
- #define TMX_FLIPPED_DIAGONALLY 0x20000000
- #define TMX_FLIPPED_FILTER (~0xE0000000)
- Image * tileset_image_load(const char * filename) {
- return (Image*) fifi_loadsimple(
- (FifiSimpleLoader*)al_load_bitmap, filename, "map", NULL);
- }
- char * tileio_get_tile_property(Bxml * xtil, char * propname) {
- Bxml * proplist, * prop, * firstprop;
- firstprop = bxml_find_child_deep(xtil, "properties", "property", NULL);
- for (prop = firstprop; prop ;
- prop = bxml_find_next(prop->sibling, "property") ) {
- char * name = bxml_get_attribute(prop, "name");
- if ((name) && (strcmp(name, propname) == 0)) {
- return bxml_get_attribute(prop, "value");
- }
- }
- return NULL;
- }
-
- int * tileio_get_tile_property_int(Bxml * xtil, char * propname, int * ivalue) {
- char * value = tileio_get_tile_property(xtil, propname);
- if (!value) return NULL;
- (*ivalue) = atoi(value);
- return ivalue;
- }
- Tile * tile_load_anim_from_xml(Tile * tile, Bxml * xtil, Tileset * set) {
- long index;
- double duration;
- Bxml * xframe = NULL;
- Bxml * xanim = bxml_find_child(xtil, "animation");
-
- /* No animations, no problem. */
- if (!xanim) return tile;
-
- for (xframe = bxml_find_child(xanim, "frame"); xframe;
- xframe = bxml_find_next(xframe->sibling, "frame")) {
- if (!bxml_get_attribute_long(xframe, "tileid", &index)) {
- LOG_WARNING("Cannot parse tileid of animation frame. Frame ignored.");
- return tile;
- }
-
- if (!bxml_get_attribute_double(xframe, "duration", &duration)) {
- LOG_WARNING("Cannot parse duration of animation frame. Frame ignored.");
- return tile;
- }
-
- /* Tiled uses ms for durations, eruta s. */
- duration /= 1000.0;
- (void) set;
- // confusingly, here we don't have to substract the tile set's firstgid
- tile_add_animation_frame(tile, index, duration);
- }
- return tile;
- }
- Tile * tile_loadxml(Bxml * xtil, Tileset * set) {
- char * sflags = NULL;
- int ianim = 0, iwait = 0, iblend = 0, ibmask = 0;
- int ilight = 0, ilmask = 0;
- int ishadow = 0, ismask = 0;
- Bxml * firstprop;
- long id ;
- Tile * tile ;
-
- if (!bxml_get_attribute_long(xtil, "id", &id)) {
- LOG_ERROR("Cannot parse tile ID.");
- return NULL;
- }
-
- tile = tileset_get(set, id);
- if (!tile) {
- LOG_ERROR("Cannot get tile from tile set %d.", id);
- return NULL;
- }
-
- // Get and apply the properties of the tile...
- /* Here, sflags sometimes is null, I guess when there is a type without a type. */
- sflags = tileio_get_tile_property(xtil, "type");
- tile_property_(tile, sflags);
- tileio_get_tile_property_int(xtil, "anim" , &ianim);
- tileio_get_tile_property_int(xtil, "wait" , &iwait);
- tileio_get_tile_property_int(xtil, "blend" , &iblend);
- tileio_get_tile_property_int(xtil, "blendmask" , &ibmask);
- tileio_get_tile_property_int(xtil, "light" , &ilight);
- tileio_get_tile_property_int(xtil, "lightmask" , &ilmask);
- tileio_get_tile_property_int(xtil, "shadow" , &ishadow);
- tileio_get_tile_property_int(xtil, "shadowmask" , &ismask);
- /* Support classic style animations... */
- if(ianim) {
- tile_anim_(tile, ianim);
- if(iwait > 0) tile_wait_(tile, iwait);
- else tile_wait_(tile, 200);
- }
- /* Support tiled-style animations as well. A renderng time these
- * will take precedence over classic animations if available. */
- tile_load_anim_from_xml(tile, xtil, set);
- tile_blend_(tile, iblend);
- tile_blend_mask_(tile, ibmask);
- tile_light_(tile, ilight);
- tile_light_mask_(tile, ilmask);
- tile_shadow_(tile, ishadow);
- tile_shadow_mask_(tile, ismask);
-
- return tile;
- }
- Tileset * tileset_loadxml(Bxml * node) {
- Bxml * xima = NULL;
- Bxml * xtil = NULL;
- long firstgid = 1;
- Image * image;
- Tileset * set;
- char * iname;
-
- if (!bxml_get_attribute_long(node, "firstgid", &firstgid)) {
- LOG_ERROR("Could not parse firstgid attribute for tileset");
- return NULL;
- }
-
- xima = bxml_find_child(node, "image"); /* Image data is in image tag. */
- if(!xima) {
- LOG_ERROR("Could not parse image tag for tileset");
- return NULL;
- }
-
- iname = bxml_get_attribute(xima, "source");
- image = tileset_image_load(iname);
- LOG("Loaded tile set: %s, %p\n", iname, image);
- // xmlFree(iname);
- if(!image) {
- LOG_ERROR("Could not load tile set source: %s", iname);
- return NULL;
- }
-
- set = tileset_new(image, firstgid);
- if(!set) {
- LOG_ERROR("Could not allocate tile set %s.", iname);
- return NULL;
- }
-
- for (xtil = bxml_find_child(node, "tile"); xtil;
- xtil = bxml_find_next(xtil->sibling, "tile")) {
- tile_loadxml(xtil, set);
- }
- return set;
- }
- /* Look up table for layer names */
- Silut tileio_layernames[] = {
- { 0, "layer_0" },
- { 1, "layer_1" },
- { 2, "layer_2" },
- { 3, "layer_3" },
- SILUT_DONE
- };
- /* Look up table for object layer names */
- Silut tileio_objectnames[] = {
- { 0, "object_0" },
- { 1, "object_1" },
- { 2, "object_2" },
- { 3, "object_3" },
- SILUT_DONE
- };
- /* Look up table for data encoding names */
- Silut tileio_encodings[] = {
- { 0, "csv" },
- SILUT_DONE
- };
- /* Gets the next value from csv. Returns NULL when done. */
- char * csv_next(char * csv, unsigned long * value) {
- char * aid = NULL;
- if(!value) return NULL;
- if(sscanf(csv, " %lu , ", value)) {
- // a number could be scanned
- aid = strchr(csv, ',');
- return aid + 1;
- }
- // no number could be scanned, stop it there
- // (*value) = XML_BAD_VALUE;
- return NULL;
- }
- /** Calculates the Allegro draw flags for a tile.
- * Diagonal flipping is unsupported
- * */
-
- int tileio_allegro_flags_for(unsigned long tileindex) {
- int result = 0;
- if (tileindex & TMX_FLIPPED_HORIZONTALLY) {
- result |= ALLEGRO_FLIP_HORIZONTAL;
- }
- if (tileindex & TMX_FLIPPED_VERTICALLY) {
- result |= ALLEGRO_FLIP_VERTICAL;
- }
-
- if (tileindex & TMX_FLIPPED_DIAGONALLY) {
- LOG_WARNING("Diagonally flipped tiles are not supported by Eruta.\n");
- // Implement this in Allegro ???
- }
-
- return result;
- }
- /** Loads a single tile pane of the tile map from xml (tmx). */
- Tilepane * tilemap_loadpanexml(Tilemap * map, Bxml * xlayer, int count) {
- long w = 0 , h = 0;
- char * name, * csv, * senc;
- Silut * laid;
- Tilepane * pane;
- Bxml * xdata;
- int layer;
- int xindex, yindex, firstgid;
-
- (void) count;
-
- firstgid = tilemap_firstgid(map);
-
- if (firstgid < 0) {
- LOG_ERROR("Tile set should be set before loading tile panes.");
- return NULL;
- }
-
- if (!bxml_get_attribute_long(xlayer, "width", &w)) {
- LOG_ERROR("Could not parse width of layer.");
- return NULL;
- }
-
- if (!bxml_get_attribute_long(xlayer, "height", &h)) {
- LOG_ERROR("Could not parse height of layer.");
- return NULL;
- }
- if ((w<0) || (w>TILEMAP_MAX_WIDTH)) {
- LOG_ERROR("Width of layer too high or negative.");
- return NULL;
- }
- if ((h<0) || (h>TILEMAP_MAX_HEIGHT)) {
- LOG_ERROR("Height of layer too high or negative.");
- return NULL;
- }
-
- name = bxml_get_attribute(xlayer, "name");
- laid = silut_lsearchcstr(tileio_layernames, name);
- if(!laid) {
- LOG_ERROR("Unknown layer name: %s.\n"
- "Must be layer_0, layer_1, layer_2 or layer_3.\n", name);
- return NULL;
- }
-
- layer = laid->integer;
- pane = tilemap_panenew(map, layer, w, h);
- if(!pane) {
- LOG_ERROR("Could not create pane.\n");
- return NULL;
- }
-
- xdata = bxml_find_child(xlayer, "data");
- if(!xdata) {
- LOG_ERROR("Could not find pane data.\n");
- return NULL;
- }
-
- senc = bxml_get_attribute(xdata, "encoding");
- laid = silut_lsearchcstr(tileio_encodings, senc);
- if(!laid) {
- LOG_ERROR("Unknown encoding: %s.\n"
- "Must be csv.\n", name);
- // xmlFree(senc);
- return NULL;
- }
- // xmlFree(senc);
- // Get first text child of <data> and hope that it is the data itself.
- // Only accept csv for now...
- csv = bxml_get_text_under(xdata);
- // csv = (char *) BxmlGetContent(xdata);
- for(yindex = 0; yindex < h; yindex ++) {
- for(xindex = 0; xindex < w; xindex ++) {
- unsigned long tileindex = 0;
- int realindex;
- int drawflags;
- csv = csv_next(csv, &tileindex);
- if(!csv) {
- LOG_ERROR("Unexpected end of csv data");
- goto csv_done;
- }
- /* We read a tile index, set it.
- * TMX's tile indexes depend on the tile set's fristgid,
- * so just substract that.
- */
- drawflags = tileio_allegro_flags_for(tileindex);
- if (drawflags) {
- LOG("Tile with flags found: %d\n", tileindex, drawflags);
- tilemap_set_flags(map, layer, xindex, yindex, drawflags);
- }
- tileindex &= TMX_FLIPPED_FILTER;
- realindex = (int)(tileindex - firstgid);
- tilemap_setindex(map, layer, xindex, yindex, realindex);
- }
- }
- csv_done:
- return pane;
- }
- /** Loads the tile panes of the tile map from xml (tmx). */
- Tilemap * tilemap_loadpanesxml(Tilemap * map, Bxml * xlayer) {
- Bxml * index;
- int count;
- if (!xlayer) return NULL;
- for(index = xlayer, count = 0; index ;
- index = bxml_find_next(index->sibling, "layer"), count++) {
- tilemap_loadpanexml(map, index, count);
- }
- return map;
- }
- /** Loads a tile map from a tmx xml document
- */
- Tilemap * tilemap_loadxml(Bxml * xml, TilemapLoadExtra * extra) {
- Tilemap * result;
- Tileset * set;
- Bxml * root = NULL;
- Bxml * xset = NULL;
- Bxml * xlayer = NULL;
- long wide = 0, high = 0;
- long tilewide = 0, tilehigh = 0;
- (void) extra;
- root = xml;
- /* Get the root element node, which should be map. */
- if (strcmp((char *) root->name, "map")) {
- LOG_ERROR("Not a tile map file!");
- return NULL;
- }
-
- if (!bxml_get_attribute_long(root, "width", &wide)) {
- LOG_ERROR("Width not a number");
- return NULL;
- }
- if (!bxml_get_attribute_long(root, "height", &high)) {
- LOG_ERROR("Height not a number!");
- return NULL;
- }
-
- if ((wide < 1) || (high < 1)) {
- LOG_ERROR("Map dimensions too small: %d %d", high, wide);
- return NULL;
- }
-
- if ((wide > TILEMAP_MAX_WIDTH) || (high > TILEMAP_MAX_HEIGHT)) {
- LOG_ERROR("Map dimensions too big : %d %d, %Dx%d at most.",
- high, wide, TILEMAP_MAX_WIDTH, TILEMAP_MAX_HEIGHT);
- return NULL;
- }
-
- if (!bxml_get_attribute_long(root, "tilewidth", &tilewide)) {
- LOG_ERROR("Could not parse tile width");
- return NULL;
- }
- if (!bxml_get_attribute_long(root, "tileheight", &tilehigh)) {
- LOG_ERROR("Could not parse tile height!");
- return NULL;
- }
-
- if ((tilewide != 32) || (tilehigh != 32)) {
- LOG_ERROR("Eruta only supports 32x32 tiles!");
- return NULL;
- }
-
- /* TODO check for map type and maybe also background color? */
-
- // Use the first tile set (and that one only) as the map's tile set.
- xset = bxml_find_child(root, "tileset");
- if (!xset) {
- bxml_show_to(root, stderr, 0);
- LOG_ERROR("Tile set not found.");
- return NULL;
- }
-
- // Look for the layers as well
- xlayer = bxml_find_child(root, "layer");
- if (!xlayer) {
- LOG_ERROR("Layers not found.");
- return NULL;
- }
- // Load the tile set
- set = tileset_loadxml(xset);
- if(!set) {
-
- LOG_ERROR("Tile set not loaded!");
- return NULL;
- }
-
- // Create a new tile map with the loaded set.
- result = tilemap_new(set, wide, high, NULL);
- // load the layers
- if(!result) {
- LOG_ERROR("Out of memory when creating new tile map to load.");
- return NULL;
- }
-
- tilemap_loadpanesxml(result, xlayer);
- return result;
- }
- /**
- * Loads a tile map from a tmx file.
- */
- Tilemap * tilemap_loadtmx(const char * filename, TilemapLoadExtra * extra) {
- Tilemap * result = NULL;
- BxmlParser * parser = bxmlparser_new();
- Bxml * xml;
-
- /* Parse the file and get the DOM */
- xml = bxmlparser_parse_filename(parser, (char *) filename);
-
- if ((xml == NULL) || (bxmlparser_get_error(parser))) {
- LOG_ERROR("error: could not parse file %s: %s\n", filename, bxmlparser_get_error(parser));
- } else {
- result = tilemap_loadxml(xml, extra);
- }
- bxmlparser_free(parser);
- bxml_free(xml);
- return result;
- }
- /**
- * Loads a tile map.
- */
- Tilemap * tilemap_load(const char * filename, TilemapLoadExtra * extra) {
- Tilemap * result;
- result = tilemap_loadtmx(filename, extra);
- /* Set up blending if loaded OK. */
- if (result) {
- tilemap_init_blend(result);
- }
- return result;
- }
- void * tilemap_fifi_load(void * extra, const char * filename) {
- return tilemap_load(filename, extra);
- }
|