Mercurial > malikania
view tools/tileset/main.cpp @ 140:a83fff870983
Client: prepare state, #712
Add update and draw function in states to allow future development of predefined
states.
author | David Demelier <markand@malikania.fr> |
---|---|
date | Wed, 27 Sep 2017 21:02:41 +0200 |
parents | 0addfab87b17 |
children | 4b292c20124c |
line wrap: on
line source
/* * main.cpp -- create malikania tilesets from tiled * * Copyright (c) 2013-2017 David Demelier <markand@malikania.fr> * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above * copyright notice and this permission notice appear in all copies. * * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #include <cassert> #include <cerrno> #include <cstring> #include <fstream> #include <iostream> #include <stdexcept> #include <boost/filesystem.hpp> #include <boost/property_tree/xml_parser.hpp> #include <json.hpp> #include <malikania/util.hpp> namespace fs = boost::filesystem; namespace ptree = boost::property_tree; namespace { /* * load_tileset * ------------------------------------------------------------------ * * In tiled maps, tilesets may be bundled in the map itself or can be in an * other file referenced by a path. * * This function check for the two case and return the XML data. */ ptree::ptree load_tileset(const fs::path& path) { ptree::ptree xml; ptree::read_xml(path.string(), xml); if (path.extension() == ".tmx") { auto tileset = xml.get_child_optional("map.tileset"); if (!tileset) throw std::runtime_error("no <tileset> node found"); // Check if it's external or embeded. auto source = tileset->get_optional<std::string>("<xmlattr>.source"); if (source) { fs::path source_path(*source); if (source_path.is_relative()) source_path = path.parent_path() / *source; ptree::read_xml(source_path.string(), xml); xml = xml.get_child("tileset"); } else xml = *tileset; } else xml = xml.get_child("tileset"); return xml; } /* * read_tileset_properties * ------------------------------------------------------------------ * * Extract all properties from the XML and return a JSON object. * * The XML looks like this: * * <tile id="123"> * <properties> * <property name="abc" value"def" /> * </properties> * </tile> */ nlohmann::json read_tileset_properties(const ptree::ptree& tree) { auto properties = tree.get_child_optional("properties"); auto json = nlohmann::json::object(); if (properties) { for (const auto& prop : *properties) { auto name = prop.second.get<std::string>("<xmlattr>.name"); auto value = prop.second.get<std::string>("<xmlattr>.value"); json[name] = value; } } return json; } /* * save_tileset_properties * ------------------------------------------------------------------ * * Read properties and add them to the JSON tile description. */ void save_tileset_properties(nlohmann::json& tiles, const std::string& id, const ptree::ptree& tree) { tiles[id]["properties"] = read_tileset_properties(tree); } /* * read_tileset_collisions * ------------------------------------------------------------------ * * Extract the collisions from the XML object description. * * This function is used for rectangle and ellipses because they use the same * attributes. */ nlohmann::json read_tileset_collision(const ptree::ptree& tree) { auto ret = nlohmann::json::object({ { "x", tree.get<double>("<xmlattr>.x") }, { "y", tree.get<double>("<xmlattr>.y") }, { "width", tree.get<double>("<xmlattr>.width") }, { "height", tree.get<double>("<xmlattr>.height") } }); if (tree.count("ellipse") > 0) ret["type"] = "ellipse"; else ret["type"] = "rectangle"; return ret; } /* * read_tileset_collision_polyline * ------------------------------------------------------------------ * * Extract the collision description from a object of type polyline. */ nlohmann::json read_tileset_collision_polyline(const ptree::ptree& tree) { auto points = tree.get<std::string>("polyline.<xmlattr>.points"); auto array = nlohmann::json::array(); for (const auto& p : mlk::util::split(points, " ")) array.push_back(std::stod(p)); return { { "type", "polyline" }, { "points", std::move(array) } }; } /* * save_tileset_collisions * ------------------------------------------------------------------ * * Read the collisions from the given XML tree and save them into the JSON * description. */ void save_tileset_collisions(nlohmann::json& tiles, const std::string& id, const ptree::ptree& tree) { auto objectgroup = tree.get_child_optional("objectgroup"); if (!objectgroup) return; auto array = nlohmann::json::array(); for (const auto& obj : *objectgroup) { if (obj.first != "object") continue; nlohmann::json collision; /* * Ellipse and rectangles are defined the same way, only check for * polylines at the moment. */ if (obj.second.count("polyline") > 0) collision = read_tileset_collision_polyline(obj.second); else collision = read_tileset_collision(obj.second); array.push_back(collision); } tiles[id]["collisions"] = std::move(array); } /* * save_tileset * ------------------------------------------------------------------ * * Save the tileset at the given path from the XML description. */ void save_tileset(const fs::path& path, const ptree::ptree& xml) { std::ofstream out(path.string(), std::ofstream::trunc); if (!out) throw std::runtime_error(std::strerror(errno)); nlohmann::json json{ { "image", xml.get<std::string>("image.<xmlattr>.source") }, { "cell", { { "width", xml.get<int>("<xmlattr>.tilewidth"), }, { "height", xml.get<int>("<xmlattr>.tileheight") } }, } }; nlohmann::json tiles = nlohmann::json::object(); for (const auto& p : xml) { if (p.first == "tile") { auto id = p.second.get<std::string>("<xmlattr>.id"); save_tileset_properties(tiles, id, p.second); save_tileset_collisions(tiles, id, p.second); } } if (!tiles.empty()) json["tiles"] = tiles; out << json.dump(4) << std::endl; } } // !namespace int main(int argc, char** argv) { -- argc; ++ argv; if (argc < 1) { std::cerr << "usage mlk-tileset input.tmx [output.json]" << std::endl; std::cerr << " mlk-tileset input.tsx [output.json]" << std::endl; return 1; } try { fs::path input(argv[0]); fs::path output; if (argc >= 2) output = argv[1]; else output = fs::path(input).replace_extension(".json"); save_tileset(output, load_tileset(input)); } catch (const std::exception& ex) { std::cerr << ex.what() << std::endl; return 1; } }