mirror of
https://github.com/ScrelliCopter/tmx2gba.git
synced 2025-02-21 03:29:25 +11:00
port to tmxlite
This commit is contained in:
15
ext/tmxlite/src/CMakeLists.txt
Normal file
15
ext/tmxlite/src/CMakeLists.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
set(PROJECT_SRC
|
||||
${PROJECT_DIR}/FreeFuncs.cpp
|
||||
${PROJECT_DIR}/ImageLayer.cpp
|
||||
${PROJECT_DIR}/Map.cpp
|
||||
${PROJECT_DIR}/Object.cpp
|
||||
${PROJECT_DIR}/ObjectGroup.cpp
|
||||
${PROJECT_DIR}/Property.cpp
|
||||
${PROJECT_DIR}/TileLayer.cpp
|
||||
${PROJECT_DIR}/LayerGroup.cpp
|
||||
${PROJECT_DIR}/Tileset.cpp
|
||||
${PROJECT_DIR}/ObjectTypes.cpp)
|
||||
|
||||
set(LIB_SRC
|
||||
${PROJECT_DIR}/miniz.c
|
||||
${PROJECT_DIR}/detail/pugixml.cpp)
|
||||
133
ext/tmxlite/src/FreeFuncs.cpp
Normal file
133
ext/tmxlite/src/FreeFuncs.cpp
Normal file
@@ -0,0 +1,133 @@
|
||||
/*********************************************************************
|
||||
Matt Marchant 2016 - 2023
|
||||
http://trederia.blogspot.com
|
||||
|
||||
tmxlite - Zlib license.
|
||||
|
||||
This software is provided 'as-is', without any express or
|
||||
implied warranty. In no event will the authors be held
|
||||
liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute
|
||||
it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented;
|
||||
you must not claim that you wrote the original software.
|
||||
If you use this software in a product, an acknowledgment
|
||||
in the product documentation would be appreciated but
|
||||
is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such,
|
||||
and must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any
|
||||
source distribution.
|
||||
*********************************************************************/
|
||||
|
||||
#ifndef USE_EXTLIBS
|
||||
#include "miniz.h"
|
||||
#else
|
||||
#include <zlib.h>
|
||||
#endif
|
||||
#include <tmxlite/FreeFuncs.hpp>
|
||||
#include <tmxlite/Types.hpp>
|
||||
#include <tmxlite/detail/Log.hpp>
|
||||
|
||||
#include <cstring>
|
||||
|
||||
bool tmx::decompress(const char* source, std::vector<unsigned char>& dest, std::size_t inSize, std::size_t expectedSize)
|
||||
{
|
||||
if (!source)
|
||||
{
|
||||
LOG("Input string is empty, decompression failed.", Logger::Type::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
//#ifdef USE_EXTLIBS
|
||||
|
||||
|
||||
//#else
|
||||
int currentSize = static_cast<int>(expectedSize);
|
||||
std::vector<unsigned char> byteArray(expectedSize / sizeof(unsigned char));
|
||||
z_stream stream;
|
||||
stream.zalloc = Z_NULL;
|
||||
stream.zfree = Z_NULL;
|
||||
stream.opaque = Z_NULL;
|
||||
stream.next_in = (Bytef*)source;
|
||||
stream.avail_in = static_cast<unsigned int>(inSize);
|
||||
stream.next_out = (Bytef*)byteArray.data();
|
||||
stream.avail_out = static_cast<unsigned int>(expectedSize);
|
||||
|
||||
//we'd prefer to use inflateInit2 but it appears
|
||||
//to be incorrect in miniz. This is fine for zlib
|
||||
//compressed data, but gzip compressed streams
|
||||
//will fail to inflate.
|
||||
#ifdef USE_EXTLIBS
|
||||
if (inflateInit2(&stream, 15 + 32) != Z_OK)
|
||||
#else
|
||||
if (inflateInit(&stream) != Z_OK)
|
||||
#endif
|
||||
{
|
||||
LOG("inflate init failed", Logger::Type::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
int result = 0;
|
||||
do
|
||||
{
|
||||
result = inflate(&stream, Z_SYNC_FLUSH);
|
||||
|
||||
switch (result)
|
||||
{
|
||||
default: break;
|
||||
case Z_NEED_DICT:
|
||||
case Z_STREAM_ERROR:
|
||||
result = Z_DATA_ERROR;
|
||||
case Z_DATA_ERROR:
|
||||
Logger::log("If using gzip or zstd compression try using zlib instead", Logger::Type::Info);
|
||||
case Z_MEM_ERROR:
|
||||
inflateEnd(&stream);
|
||||
Logger::log("inflate() returned " + std::to_string(result), Logger::Type::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (result != Z_STREAM_END)
|
||||
{
|
||||
int oldSize = currentSize;
|
||||
currentSize *= 2;
|
||||
std::vector<unsigned char> newArray(currentSize / sizeof(unsigned char));
|
||||
std::memcpy(newArray.data(), byteArray.data(), currentSize / 2);
|
||||
byteArray = std::move(newArray);
|
||||
|
||||
stream.next_out = (Bytef*)(byteArray.data() + oldSize);
|
||||
stream.avail_out = oldSize;
|
||||
|
||||
}
|
||||
} while (result != Z_STREAM_END);
|
||||
|
||||
if (stream.avail_in != 0)
|
||||
{
|
||||
LOG("stream.avail_in is 0", Logger::Type::Error);
|
||||
LOG("zlib decompression failed.", Logger::Type::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
const int outSize = currentSize - stream.avail_out;
|
||||
inflateEnd(&stream);
|
||||
|
||||
std::vector<unsigned char> newArray(outSize / sizeof(unsigned char));
|
||||
std::memcpy(newArray.data(), byteArray.data(), outSize);
|
||||
byteArray = std::move(newArray);
|
||||
|
||||
//copy bytes to vector
|
||||
dest.insert(dest.begin(), byteArray.begin(), byteArray.end());
|
||||
//#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
std::ostream& operator << (std::ostream& os, const tmx::Colour& c)
|
||||
{
|
||||
os << "RGBA: " << (int)c.r << ", " << (int)c.g << ", " << (int)c.b << ", " << (int)c.a;
|
||||
return os;
|
||||
}
|
||||
110
ext/tmxlite/src/ImageLayer.cpp
Normal file
110
ext/tmxlite/src/ImageLayer.cpp
Normal file
@@ -0,0 +1,110 @@
|
||||
/*********************************************************************
|
||||
Matt Marchant 2016 - 2023
|
||||
http://trederia.blogspot.com
|
||||
|
||||
tmxlite - Zlib license.
|
||||
|
||||
This software is provided 'as-is', without any express or
|
||||
implied warranty. In no event will the authors be held
|
||||
liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute
|
||||
it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented;
|
||||
you must not claim that you wrote the original software.
|
||||
If you use this software in a product, an acknowledgment
|
||||
in the product documentation would be appreciated but
|
||||
is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such,
|
||||
and must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any
|
||||
source distribution.
|
||||
*********************************************************************/
|
||||
|
||||
#ifdef USE_EXTLIBS
|
||||
#include <pugixml.hpp>
|
||||
#else
|
||||
#include "detail/pugixml.hpp"
|
||||
#endif
|
||||
#include <tmxlite/ImageLayer.hpp>
|
||||
#include <tmxlite/FreeFuncs.hpp>
|
||||
#include <tmxlite/detail/Log.hpp>
|
||||
|
||||
using namespace tmx;
|
||||
|
||||
ImageLayer::ImageLayer(const std::string& workingDir)
|
||||
: m_workingDir (workingDir),
|
||||
m_hasTransparency (false),
|
||||
m_hasRepeatX (false),
|
||||
m_hasRepeatY (false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//public
|
||||
void ImageLayer::parse(const pugi::xml_node& node, Map*)
|
||||
{
|
||||
std::string attribName = node.name();
|
||||
if (attribName != "imagelayer")
|
||||
{
|
||||
Logger::log("Node not an image layer, node skipped", Logger::Type::Error);
|
||||
return;
|
||||
}
|
||||
|
||||
//TODO this gets repeated foreach layer type and could all be moved to base class...
|
||||
setName(node.attribute("name").as_string());
|
||||
setClass(node.attribute("class").as_string());
|
||||
setOpacity(node.attribute("opacity").as_float(1.f));
|
||||
setVisible(node.attribute("visible").as_bool(true));
|
||||
setOffset(node.attribute("offsetx").as_int(0), node.attribute("offsety").as_int(0));
|
||||
setSize(node.attribute("width").as_uint(0), node.attribute("height").as_uint(0));
|
||||
setParallaxFactor(node.attribute("parallaxx").as_float(1.f), node.attribute("parallaxy").as_float(1.f));
|
||||
|
||||
std::string tintColour = node.attribute("tintcolor").as_string();
|
||||
if (!tintColour.empty())
|
||||
{
|
||||
setTintColour(colourFromString(tintColour));
|
||||
}
|
||||
|
||||
m_hasRepeatX = node.attribute("repeatx").as_bool(false);
|
||||
m_hasRepeatY = node.attribute("repeaty").as_bool(false);
|
||||
|
||||
for (const auto& child : node.children())
|
||||
{
|
||||
attribName = child.name();
|
||||
if (attribName == "image")
|
||||
{
|
||||
attribName = child.attribute("source").as_string();
|
||||
if (attribName.empty())
|
||||
{
|
||||
Logger::log("Image Layer has missing source property", Logger::Type::Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
if (child.attribute("width") && child.attribute("height"))
|
||||
{
|
||||
m_imageSize.x = child.attribute("width").as_uint();
|
||||
m_imageSize.y = child.attribute("height").as_uint();
|
||||
}
|
||||
|
||||
m_filePath = resolveFilePath(attribName, m_workingDir);
|
||||
if (child.attribute("trans"))
|
||||
{
|
||||
attribName = child.attribute("trans").as_string();
|
||||
m_transparencyColour = colourFromString(attribName);
|
||||
m_hasTransparency = true;
|
||||
}
|
||||
}
|
||||
else if (attribName == "properties")
|
||||
{
|
||||
for (const auto& p : child.children())
|
||||
{
|
||||
addProperty(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
109
ext/tmxlite/src/LayerGroup.cpp
Normal file
109
ext/tmxlite/src/LayerGroup.cpp
Normal file
@@ -0,0 +1,109 @@
|
||||
/*********************************************************************
|
||||
Grant Gangi 2019
|
||||
Matt Marchant 2023
|
||||
|
||||
tmxlite - Zlib license.
|
||||
|
||||
This software is provided 'as-is', without any express or
|
||||
implied warranty. In no event will the authors be held
|
||||
liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute
|
||||
it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented;
|
||||
you must not claim that you wrote the original software.
|
||||
If you use this software in a product, an acknowledgment
|
||||
in the product documentation would be appreciated but
|
||||
is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such,
|
||||
and must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any
|
||||
source distribution.
|
||||
*********************************************************************/
|
||||
|
||||
#ifdef USE_EXTLIBS
|
||||
#include <pugixml.hpp>
|
||||
#else
|
||||
#include "detail/pugixml.hpp"
|
||||
#endif
|
||||
#include <tmxlite/LayerGroup.hpp>
|
||||
#include <tmxlite/FreeFuncs.hpp>
|
||||
#include <tmxlite/ObjectGroup.hpp>
|
||||
#include <tmxlite/ImageLayer.hpp>
|
||||
#include <tmxlite/TileLayer.hpp>
|
||||
#include <tmxlite/detail/Log.hpp>
|
||||
|
||||
using namespace tmx;
|
||||
|
||||
LayerGroup::LayerGroup(const std::string& workingDir, const Vector2u& tileCount)
|
||||
: m_workingDir(workingDir),
|
||||
m_tileCount(tileCount)
|
||||
{
|
||||
}
|
||||
|
||||
//public
|
||||
void LayerGroup::parse(const pugi::xml_node& node, Map* map)
|
||||
{
|
||||
assert(map);
|
||||
std::string attribString = node.name();
|
||||
if (attribString != "group")
|
||||
{
|
||||
Logger::log("Node was not a group layer, node will be skipped.", Logger::Type::Error);
|
||||
return;
|
||||
}
|
||||
|
||||
setName(node.attribute("name").as_string());
|
||||
setClass(node.attribute("class").as_string());
|
||||
setOpacity(node.attribute("opacity").as_float(1.f));
|
||||
setVisible(node.attribute("visible").as_bool(true));
|
||||
setOffset(node.attribute("offsetx").as_int(0), node.attribute("offsety").as_int(0));
|
||||
setSize(node.attribute("width").as_uint(0), node.attribute("height").as_uint(0));
|
||||
setParallaxFactor(node.attribute("parallaxx").as_float(1.f), node.attribute("parallaxy").as_float(1.f));
|
||||
|
||||
std::string tintColour = node.attribute("tintcolor").as_string();
|
||||
if (!tintColour.empty())
|
||||
{
|
||||
setTintColour(colourFromString(tintColour));
|
||||
}
|
||||
|
||||
// parse children
|
||||
for (const auto& child : node.children())
|
||||
{
|
||||
attribString = child.name();
|
||||
if (attribString == "properties")
|
||||
{
|
||||
for (const auto& p : child.children())
|
||||
{
|
||||
addProperty(p);
|
||||
}
|
||||
}
|
||||
else if (attribString == "layer")
|
||||
{
|
||||
m_layers.emplace_back(std::make_unique<TileLayer>(m_tileCount.x * m_tileCount.y));
|
||||
m_layers.back()->parse(child, map);
|
||||
}
|
||||
else if (attribString == "objectgroup")
|
||||
{
|
||||
m_layers.emplace_back(std::make_unique<ObjectGroup>());
|
||||
m_layers.back()->parse(child, map);
|
||||
}
|
||||
else if (attribString == "imagelayer")
|
||||
{
|
||||
m_layers.emplace_back(std::make_unique<ImageLayer>(m_workingDir));
|
||||
m_layers.back()->parse(child, map);
|
||||
}
|
||||
else if (attribString == "group")
|
||||
{
|
||||
m_layers.emplace_back(std::make_unique<LayerGroup>(m_workingDir, m_tileCount));
|
||||
m_layers.back()->parse(child, map);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("Unidentified name " + attribString + ": node skipped", Logger::Type::Warning);
|
||||
}
|
||||
}
|
||||
}
|
||||
367
ext/tmxlite/src/Map.cpp
Normal file
367
ext/tmxlite/src/Map.cpp
Normal file
@@ -0,0 +1,367 @@
|
||||
/*********************************************************************
|
||||
Matt Marchant 2016 - 2023
|
||||
http://trederia.blogspot.com
|
||||
|
||||
tmxlite - Zlib license.
|
||||
|
||||
This software is provided 'as-is', without any express or
|
||||
implied warranty. In no event will the authors be held
|
||||
liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute
|
||||
it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented;
|
||||
you must not claim that you wrote the original software.
|
||||
If you use this software in a product, an acknowledgment
|
||||
in the product documentation would be appreciated but
|
||||
is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such,
|
||||
and must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any
|
||||
source distribution.
|
||||
*********************************************************************/
|
||||
|
||||
#ifdef USE_EXTLIBS
|
||||
#include <pugixml.hpp>
|
||||
#else
|
||||
#include "detail/pugixml.hpp"
|
||||
#endif
|
||||
#include <tmxlite/Map.hpp>
|
||||
#include <tmxlite/FreeFuncs.hpp>
|
||||
#include <tmxlite/ObjectGroup.hpp>
|
||||
#include <tmxlite/ImageLayer.hpp>
|
||||
#include <tmxlite/TileLayer.hpp>
|
||||
#include <tmxlite/LayerGroup.hpp>
|
||||
#include <tmxlite/detail/Log.hpp>
|
||||
#include <tmxlite/detail/Android.hpp>
|
||||
|
||||
#include <queue>
|
||||
|
||||
using namespace tmx;
|
||||
|
||||
Map::Map()
|
||||
: m_orientation (Orientation::None),
|
||||
m_renderOrder (RenderOrder::None),
|
||||
m_infinite (false),
|
||||
m_hexSideLength (0.f),
|
||||
m_staggerAxis (StaggerAxis::None),
|
||||
m_staggerIndex (StaggerIndex::None)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//public
|
||||
bool Map::load(const std::string& path)
|
||||
{
|
||||
reset();
|
||||
|
||||
//open the doc
|
||||
pugi::xml_document doc;
|
||||
auto result = doc.load_file(path.c_str());
|
||||
if (!result)
|
||||
{
|
||||
Logger::log("Failed opening " + path, Logger::Type::Error);
|
||||
Logger::log("Reason: " + std::string(result.description()), Logger::Type::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
//make sure we have consistent path separators
|
||||
m_workingDirectory = path;
|
||||
std::replace(m_workingDirectory.begin(), m_workingDirectory.end(), '\\', '/');
|
||||
m_workingDirectory = getFilePath(m_workingDirectory);
|
||||
|
||||
if (!m_workingDirectory.empty() &&
|
||||
m_workingDirectory.back() == '/')
|
||||
{
|
||||
m_workingDirectory.pop_back();
|
||||
}
|
||||
|
||||
|
||||
//find the map node and bail if it doesn't exist
|
||||
auto mapNode = doc.child("map");
|
||||
if (!mapNode)
|
||||
{
|
||||
Logger::log("Failed opening map: " + path + ", no map node found", Logger::Type::Error);
|
||||
return reset();
|
||||
}
|
||||
|
||||
return parseMapNode(mapNode);
|
||||
}
|
||||
|
||||
bool Map::loadFromString(const std::string& data, const std::string& workingDir)
|
||||
{
|
||||
reset();
|
||||
|
||||
//open the doc
|
||||
pugi::xml_document doc;
|
||||
auto result = doc.load_string(data.c_str());
|
||||
if (!result)
|
||||
{
|
||||
Logger::log("Failed opening map", Logger::Type::Error);
|
||||
Logger::log("Reason: " + std::string(result.description()), Logger::Type::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
//make sure we have consistent path separators
|
||||
m_workingDirectory = workingDir;
|
||||
std::replace(m_workingDirectory.begin(), m_workingDirectory.end(), '\\', '/');
|
||||
m_workingDirectory = getFilePath(m_workingDirectory);
|
||||
|
||||
if (!m_workingDirectory.empty() &&
|
||||
m_workingDirectory.back() == '/')
|
||||
{
|
||||
m_workingDirectory.pop_back();
|
||||
}
|
||||
|
||||
//find the map node and bail if it doesn't exist
|
||||
auto mapNode = doc.child("map");
|
||||
if (!mapNode)
|
||||
{
|
||||
Logger::log("Failed opening map: no map node found", Logger::Type::Error);
|
||||
return reset();
|
||||
}
|
||||
|
||||
return parseMapNode(mapNode);
|
||||
}
|
||||
|
||||
//private
|
||||
bool Map::parseMapNode(const pugi::xml_node& mapNode)
|
||||
{
|
||||
//parse map attributes
|
||||
std::size_t pointPos = 0;
|
||||
std::string attribString = mapNode.attribute("version").as_string();
|
||||
if (attribString.empty() || (pointPos = attribString.find('.')) == std::string::npos)
|
||||
{
|
||||
Logger::log("Invalid map version value, map not loaded.", Logger::Type::Error);
|
||||
return reset();
|
||||
}
|
||||
|
||||
m_version.upper = STOI(attribString.substr(0, pointPos));
|
||||
m_version.lower = STOI(attribString.substr(pointPos + 1));
|
||||
|
||||
m_class = mapNode.attribute("class").as_string();
|
||||
|
||||
attribString = mapNode.attribute("orientation").as_string();
|
||||
if (attribString.empty())
|
||||
{
|
||||
Logger::log("Missing map orientation attribute, map not loaded.", Logger::Type::Error);
|
||||
return reset();
|
||||
}
|
||||
|
||||
if (attribString == "orthogonal")
|
||||
{
|
||||
m_orientation = Orientation::Orthogonal;
|
||||
}
|
||||
else if (attribString == "isometric")
|
||||
{
|
||||
m_orientation = Orientation::Isometric;
|
||||
}
|
||||
else if (attribString == "staggered")
|
||||
{
|
||||
m_orientation = Orientation::Staggered;
|
||||
}
|
||||
else if (attribString == "hexagonal")
|
||||
{
|
||||
m_orientation = Orientation::Hexagonal;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::log(attribString + " format maps aren't supported yet, sorry! Map not loaded", Logger::Type::Error);
|
||||
return reset();
|
||||
}
|
||||
|
||||
attribString = mapNode.attribute("renderorder").as_string();
|
||||
//this property is optional for older version of map files
|
||||
if (!attribString.empty())
|
||||
{
|
||||
if (attribString == "right-down")
|
||||
{
|
||||
m_renderOrder = RenderOrder::RightDown;
|
||||
}
|
||||
else if (attribString == "right-up")
|
||||
{
|
||||
m_renderOrder = RenderOrder::RightUp;
|
||||
}
|
||||
else if (attribString == "left-down")
|
||||
{
|
||||
m_renderOrder = RenderOrder::LeftDown;
|
||||
}
|
||||
else if (attribString == "left-up")
|
||||
{
|
||||
m_renderOrder = RenderOrder::LeftUp;
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::log(attribString + ": invalid render order. Map not loaded.", Logger::Type::Error);
|
||||
return reset();
|
||||
}
|
||||
}
|
||||
|
||||
if (mapNode.attribute("infinite"))
|
||||
{
|
||||
m_infinite = mapNode.attribute("infinite").as_int() != 0;
|
||||
}
|
||||
|
||||
unsigned width = mapNode.attribute("width").as_int();
|
||||
unsigned height = mapNode.attribute("height").as_int();
|
||||
if (width && height)
|
||||
{
|
||||
m_tileCount = { width, height };
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::log("Invalid map tile count, map not loaded", Logger::Type::Error);
|
||||
return reset();
|
||||
}
|
||||
|
||||
width = mapNode.attribute("tilewidth").as_int();
|
||||
height = mapNode.attribute("tileheight").as_int();
|
||||
if (width && height)
|
||||
{
|
||||
m_tileSize = { width, height };
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::log("Invalid tile size, map not loaded", Logger::Type::Error);
|
||||
return reset();
|
||||
}
|
||||
|
||||
m_hexSideLength = mapNode.attribute("hexsidelength").as_float();
|
||||
if (m_orientation == Orientation::Hexagonal && m_hexSideLength <= 0)
|
||||
{
|
||||
Logger::log("Invalid he side length found, map not loaded", Logger::Type::Error);
|
||||
return reset();
|
||||
}
|
||||
|
||||
attribString = mapNode.attribute("staggeraxis").as_string();
|
||||
if (attribString == "x")
|
||||
{
|
||||
m_staggerAxis = StaggerAxis::X;
|
||||
}
|
||||
else if (attribString == "y")
|
||||
{
|
||||
m_staggerAxis = StaggerAxis::Y;
|
||||
}
|
||||
if ((m_orientation == Orientation::Staggered || m_orientation == Orientation::Hexagonal)
|
||||
&& m_staggerAxis == StaggerAxis::None)
|
||||
{
|
||||
Logger::log("Map missing stagger axis property. Map not loaded.", Logger::Type::Error);
|
||||
return reset();
|
||||
}
|
||||
|
||||
attribString = mapNode.attribute("staggerindex").as_string();
|
||||
if (attribString == "odd")
|
||||
{
|
||||
m_staggerIndex = StaggerIndex::Odd;
|
||||
}
|
||||
else if (attribString == "even")
|
||||
{
|
||||
m_staggerIndex = StaggerIndex::Even;
|
||||
}
|
||||
if ((m_orientation == Orientation::Staggered || m_orientation == Orientation::Hexagonal)
|
||||
&& m_staggerIndex == StaggerIndex::None)
|
||||
{
|
||||
Logger::log("Map missing stagger index property. Map not loaded.", Logger::Type::Error);
|
||||
return reset();
|
||||
}
|
||||
|
||||
m_parallaxOrigin =
|
||||
{
|
||||
mapNode.attribute("parallaxoriginx").as_float(0.f),
|
||||
mapNode.attribute("parallaxoriginy").as_float(0.f)
|
||||
};
|
||||
|
||||
//colour property is optional
|
||||
attribString = mapNode.attribute("backgroundcolor").as_string();
|
||||
if (!attribString.empty())
|
||||
{
|
||||
m_backgroundColour = colourFromString(attribString);
|
||||
}
|
||||
|
||||
//TODO do we need next object ID
|
||||
|
||||
//parse all child nodes
|
||||
for (const auto& node : mapNode.children())
|
||||
{
|
||||
std::string name = node.name();
|
||||
if (name == "tileset")
|
||||
{
|
||||
m_tilesets.emplace_back(m_workingDirectory);
|
||||
m_tilesets.back().parse(node, this);
|
||||
}
|
||||
else if (name == "layer")
|
||||
{
|
||||
m_layers.emplace_back(std::make_unique<TileLayer>(m_tileCount.x * m_tileCount.y));
|
||||
m_layers.back()->parse(node);
|
||||
}
|
||||
else if (name == "objectgroup")
|
||||
{
|
||||
m_layers.emplace_back(std::make_unique<ObjectGroup>());
|
||||
m_layers.back()->parse(node, this);
|
||||
}
|
||||
else if (name == "imagelayer")
|
||||
{
|
||||
m_layers.emplace_back(std::make_unique<ImageLayer>(m_workingDirectory));
|
||||
m_layers.back()->parse(node, this);
|
||||
}
|
||||
else if (name == "properties")
|
||||
{
|
||||
const auto& children = node.children();
|
||||
for (const auto& child : children)
|
||||
{
|
||||
m_properties.emplace_back();
|
||||
m_properties.back().parse(child);
|
||||
}
|
||||
}
|
||||
else if (name == "group")
|
||||
{
|
||||
m_layers.emplace_back(std::make_unique<LayerGroup>(m_workingDirectory, m_tileCount));
|
||||
m_layers.back()->parse(node, this);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("Unidentified name " + name + ": node skipped", Logger::Type::Warning);
|
||||
}
|
||||
}
|
||||
// fill animated tiles for easier lookup into map
|
||||
for(const auto& ts : m_tilesets)
|
||||
{
|
||||
for(const auto& tile : ts.getTiles())
|
||||
{
|
||||
if (!tile.animation.frames.empty())
|
||||
{
|
||||
m_animTiles[tile.ID + ts.getFirstGID()] = tile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Map::reset()
|
||||
{
|
||||
m_orientation = Orientation::None;
|
||||
m_renderOrder = RenderOrder::None;
|
||||
m_tileCount = { 0u, 0u };
|
||||
m_tileSize = { 0u, 0u };
|
||||
m_hexSideLength = 0.f;
|
||||
m_staggerAxis = StaggerAxis::None;
|
||||
m_staggerIndex = StaggerIndex::None;
|
||||
m_backgroundColour = {};
|
||||
m_workingDirectory = "";
|
||||
|
||||
m_tilesets.clear();
|
||||
m_layers.clear();
|
||||
m_properties.clear();
|
||||
|
||||
m_templateObjects.clear();
|
||||
m_templateTilesets.clear();
|
||||
|
||||
m_animTiles.clear();
|
||||
|
||||
return false;
|
||||
}
|
||||
403
ext/tmxlite/src/Object.cpp
Normal file
403
ext/tmxlite/src/Object.cpp
Normal file
@@ -0,0 +1,403 @@
|
||||
/*********************************************************************
|
||||
Matt Marchant 2016 - 2021
|
||||
http://trederia.blogspot.com
|
||||
|
||||
tmxlite - Zlib license.
|
||||
|
||||
This software is provided 'as-is', without any express or
|
||||
implied warranty. In no event will the authors be held
|
||||
liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute
|
||||
it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented;
|
||||
you must not claim that you wrote the original software.
|
||||
If you use this software in a product, an acknowledgment
|
||||
in the product documentation would be appreciated but
|
||||
is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such,
|
||||
and must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any
|
||||
source distribution.
|
||||
*********************************************************************/
|
||||
|
||||
#ifdef USE_EXTLIBS
|
||||
#include <pugixml.hpp>
|
||||
#else
|
||||
#include "detail/pugixml.hpp"
|
||||
#endif
|
||||
#include <tmxlite/Object.hpp>
|
||||
#include <tmxlite/FreeFuncs.hpp>
|
||||
#include <tmxlite/Map.hpp>
|
||||
#include <tmxlite/Tileset.hpp>
|
||||
#include <tmxlite/detail/Log.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace tmx;
|
||||
|
||||
Object::Object()
|
||||
: m_UID (0),
|
||||
m_rotation (0.f),
|
||||
m_tileID (0),
|
||||
m_flipFlags (0),
|
||||
m_visible (true),
|
||||
m_shape (Shape::Rectangle)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//public
|
||||
void Object::parse(const pugi::xml_node& node, Map* map)
|
||||
{
|
||||
std::string attribString = node.name();
|
||||
if (attribString != "object")
|
||||
{
|
||||
Logger::log("This not an Object node, parsing skipped.", Logger::Type::Error);
|
||||
return;
|
||||
}
|
||||
|
||||
m_UID = node.attribute("id").as_int();
|
||||
m_name = node.attribute("name").as_string();
|
||||
|
||||
m_class = node.attribute("type").as_string();
|
||||
if (m_class.empty())
|
||||
{
|
||||
m_class = node.attribute("class").as_string();
|
||||
}
|
||||
|
||||
m_position.x = node.attribute("x").as_float();
|
||||
m_AABB.left = m_position.x;
|
||||
m_position.y = node.attribute("y").as_float();
|
||||
m_AABB.top = m_position.y;
|
||||
m_AABB.width = node.attribute("width").as_float();
|
||||
m_AABB.height = node.attribute("height").as_float();
|
||||
m_rotation = node.attribute("rotation").as_float();
|
||||
m_visible = node.attribute("visible").as_bool(true);
|
||||
|
||||
m_tileID = node.attribute("gid").as_uint();
|
||||
|
||||
static const std::uint32_t mask = 0xf0000000;
|
||||
m_flipFlags = ((m_tileID & mask) >> 28);
|
||||
m_tileID = m_tileID & ~mask;
|
||||
|
||||
for (const auto& child : node.children())
|
||||
{
|
||||
attribString = child.name();
|
||||
if (attribString == "properties")
|
||||
{
|
||||
for (const auto& p : child.children())
|
||||
{
|
||||
m_properties.emplace_back();
|
||||
m_properties.back().parse(p);
|
||||
}
|
||||
}
|
||||
else if (attribString == "ellipse")
|
||||
{
|
||||
m_shape = Shape::Ellipse;
|
||||
}
|
||||
else if (attribString == "point")
|
||||
{
|
||||
m_shape = Shape::Point;
|
||||
}
|
||||
else if (attribString == "polygon")
|
||||
{
|
||||
m_shape = Shape::Polygon;
|
||||
parsePoints(child);
|
||||
}
|
||||
else if (attribString == "polyline")
|
||||
{
|
||||
m_shape = Shape::Polyline;
|
||||
parsePoints(child);
|
||||
}
|
||||
else if (attribString == "text")
|
||||
{
|
||||
m_shape = Shape::Text;
|
||||
parseText(child);
|
||||
}
|
||||
}
|
||||
|
||||
//parse templates last so we know which properties
|
||||
//ought to be overridden
|
||||
std::string templateStr = node.attribute("template").as_string();
|
||||
if (!templateStr.empty() && map)
|
||||
{
|
||||
parseTemplate(templateStr, map);
|
||||
}
|
||||
}
|
||||
|
||||
//private
|
||||
void Object::parsePoints(const pugi::xml_node& node)
|
||||
{
|
||||
if (node.attribute("points"))
|
||||
{
|
||||
std::string pointlist = node.attribute("points").as_string();
|
||||
std::stringstream stream(pointlist);
|
||||
std::vector<std::string> points;
|
||||
std::string pointstring;
|
||||
while (std::getline(stream, pointstring, ' '))
|
||||
{
|
||||
points.push_back(pointstring);
|
||||
}
|
||||
|
||||
//parse each pair into sf::vector2f
|
||||
for (unsigned int i = 0; i < points.size(); i++)
|
||||
{
|
||||
std::vector<float> coords;
|
||||
std::stringstream coordstream(points[i]);
|
||||
|
||||
float j;
|
||||
while (coordstream >> j)
|
||||
{
|
||||
coords.push_back(j);
|
||||
//TODO this should really ignore anything non-numeric
|
||||
if (coordstream.peek() == ',')
|
||||
{
|
||||
coordstream.ignore();
|
||||
}
|
||||
}
|
||||
m_points.emplace_back(coords[0], coords[1]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger::log("Points for polygon or polyline object are missing", Logger::Type::Warning);
|
||||
}
|
||||
}
|
||||
|
||||
void Object::parseText(const pugi::xml_node& node)
|
||||
{
|
||||
m_textData.bold = node.attribute("bold").as_bool(false);
|
||||
m_textData.colour = colourFromString(node.attribute("color").as_string("#FFFFFFFF"));
|
||||
m_textData.fontFamily = node.attribute("fontfamily").as_string();
|
||||
m_textData.italic = node.attribute("italic").as_bool(false);
|
||||
m_textData.kerning = node.attribute("kerning").as_bool(true);
|
||||
m_textData.pixelSize = node.attribute("pixelsize").as_uint(16);
|
||||
m_textData.strikethough = node.attribute("strikeout").as_bool(false);
|
||||
m_textData.underline = node.attribute("underline").as_bool(false);
|
||||
m_textData.wrap = node.attribute("wrap").as_bool(false);
|
||||
|
||||
std::string alignment = node.attribute("halign").as_string("left");
|
||||
if (alignment == "left")
|
||||
{
|
||||
m_textData.hAlign = Text::HAlign::Left;
|
||||
}
|
||||
else if (alignment == "center")
|
||||
{
|
||||
m_textData.hAlign = Text::HAlign::Centre;
|
||||
}
|
||||
else if (alignment == "right")
|
||||
{
|
||||
m_textData.hAlign = Text::HAlign::Right;
|
||||
}
|
||||
|
||||
alignment = node.attribute("valign").as_string("top");
|
||||
if (alignment == "top")
|
||||
{
|
||||
m_textData.vAlign = Text::VAlign::Top;
|
||||
}
|
||||
else if (alignment == "center")
|
||||
{
|
||||
m_textData.vAlign = Text::VAlign::Centre;
|
||||
}
|
||||
else if (alignment == "bottom")
|
||||
{
|
||||
m_textData.vAlign = Text::VAlign::Bottom;
|
||||
}
|
||||
|
||||
m_textData.content = node.text().as_string();
|
||||
}
|
||||
|
||||
void Object::parseTemplate(const std::string& path, Map* map)
|
||||
{
|
||||
assert(map);
|
||||
|
||||
auto& templateObjects = map->getTemplateObjects();
|
||||
auto& templateTilesets = map->getTemplateTilesets();
|
||||
|
||||
//load the template if not already loaded
|
||||
if (templateObjects.count(path) == 0)
|
||||
{
|
||||
auto templatePath = map->getWorkingDirectory() + "/" + path;
|
||||
|
||||
pugi::xml_document doc;
|
||||
if (!doc.load_file(templatePath.c_str()))
|
||||
{
|
||||
Logger::log("Failed opening template file " + path, Logger::Type::Error);
|
||||
return;
|
||||
}
|
||||
|
||||
auto templateNode = doc.child("template");
|
||||
if (!templateNode)
|
||||
{
|
||||
Logger::log("Template node missing from " + path, Logger::Type::Error);
|
||||
return;
|
||||
}
|
||||
|
||||
//if the template has a tileset load that (if not already loaded)
|
||||
std::string tilesetName;
|
||||
auto tileset = templateNode.child("tileset");
|
||||
if (tileset)
|
||||
{
|
||||
tilesetName = tileset.attribute("source").as_string();
|
||||
if (!tilesetName.empty() &&
|
||||
templateTilesets.count(tilesetName) == 0)
|
||||
{
|
||||
templateTilesets.insert(std::make_pair(tilesetName, Tileset(map->getWorkingDirectory())));
|
||||
templateTilesets.at(tilesetName).parse(tileset, map);
|
||||
}
|
||||
}
|
||||
|
||||
//parse the object - don't pass the map pointer here so there's
|
||||
//no recursion if someone tried to get clever and put a template in a template
|
||||
auto obj = templateNode.child("object");
|
||||
if (obj)
|
||||
{
|
||||
templateObjects.insert(std::make_pair(path, Object()));
|
||||
templateObjects[path].parse(obj, nullptr);
|
||||
templateObjects[path].m_tilesetName = tilesetName;
|
||||
}
|
||||
}
|
||||
|
||||
//apply any non-overridden object properties from the template
|
||||
if (templateObjects.count(path) != 0)
|
||||
{
|
||||
const auto& obj = templateObjects[path];
|
||||
if (m_AABB.width == 0)
|
||||
{
|
||||
m_AABB.width = obj.m_AABB.width;
|
||||
}
|
||||
|
||||
if (m_AABB.height == 0)
|
||||
{
|
||||
m_AABB.height = obj.m_AABB.height;
|
||||
}
|
||||
|
||||
m_tilesetName = obj.m_tilesetName;
|
||||
|
||||
if (m_name.empty())
|
||||
{
|
||||
m_name = obj.m_name;
|
||||
}
|
||||
|
||||
if (m_class.empty())
|
||||
{
|
||||
m_class = obj.m_class;
|
||||
}
|
||||
|
||||
if (m_rotation == 0)
|
||||
{
|
||||
m_rotation = obj.m_rotation;
|
||||
}
|
||||
|
||||
if (m_tileID == 0)
|
||||
{
|
||||
m_tileID = obj.m_tileID;
|
||||
}
|
||||
|
||||
if (m_flipFlags == 0)
|
||||
{
|
||||
m_flipFlags = obj.m_flipFlags;
|
||||
}
|
||||
|
||||
if (m_shape == Shape::Rectangle)
|
||||
{
|
||||
m_shape = obj.m_shape;
|
||||
}
|
||||
|
||||
if (m_points.empty())
|
||||
{
|
||||
m_points = obj.m_points;
|
||||
}
|
||||
|
||||
//compare properties and only copy ones that don't exist
|
||||
for (const auto& p : obj.m_properties)
|
||||
{
|
||||
auto result = std::find_if(m_properties.begin(), m_properties.end(),
|
||||
[&p](const Property& a)
|
||||
{
|
||||
return a.getName() == p.getName();
|
||||
});
|
||||
|
||||
if (result == m_properties.end())
|
||||
{
|
||||
m_properties.push_back(p);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (m_shape == Shape::Text)
|
||||
{
|
||||
//check each text property and update as necessary
|
||||
//TODO this makes he assumption we prefer the template
|
||||
//properties over the default ones - this might not
|
||||
//actually be the case....
|
||||
const auto& otherText = obj.m_textData;
|
||||
if (m_textData.fontFamily.empty())
|
||||
{
|
||||
m_textData.fontFamily = otherText.fontFamily;
|
||||
}
|
||||
|
||||
if (m_textData.pixelSize == 16)
|
||||
{
|
||||
m_textData.pixelSize = otherText.pixelSize;
|
||||
}
|
||||
|
||||
//TODO this isn't actually right if we *want* to be false
|
||||
//and the template is set to true...
|
||||
if (m_textData.wrap == false)
|
||||
{
|
||||
m_textData.wrap = otherText.wrap;
|
||||
}
|
||||
|
||||
if (m_textData.colour == Colour())
|
||||
{
|
||||
m_textData.colour = otherText.colour;
|
||||
}
|
||||
|
||||
if (m_textData.bold == false)
|
||||
{
|
||||
m_textData.bold = otherText.bold;
|
||||
}
|
||||
|
||||
if (m_textData.italic == false)
|
||||
{
|
||||
m_textData.italic = otherText.italic;
|
||||
}
|
||||
|
||||
if (m_textData.underline == false)
|
||||
{
|
||||
m_textData.underline = otherText.underline;
|
||||
}
|
||||
|
||||
if (m_textData.strikethough == false)
|
||||
{
|
||||
m_textData.strikethough = otherText.strikethough;
|
||||
}
|
||||
|
||||
if (m_textData.kerning == true)
|
||||
{
|
||||
m_textData.kerning = otherText.kerning;
|
||||
}
|
||||
|
||||
if (m_textData.hAlign == Text::HAlign::Left)
|
||||
{
|
||||
m_textData.hAlign = otherText.hAlign;
|
||||
}
|
||||
|
||||
if (m_textData.vAlign == Text::VAlign::Top)
|
||||
{
|
||||
m_textData.vAlign = otherText.vAlign;
|
||||
}
|
||||
|
||||
if (m_textData.content.empty())
|
||||
{
|
||||
m_textData.content = otherText.content;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
102
ext/tmxlite/src/ObjectGroup.cpp
Normal file
102
ext/tmxlite/src/ObjectGroup.cpp
Normal file
@@ -0,0 +1,102 @@
|
||||
/*********************************************************************
|
||||
Matt Marchant 2016 - 2023
|
||||
http://trederia.blogspot.com
|
||||
|
||||
tmxlite - Zlib license.
|
||||
|
||||
This software is provided 'as-is', without any express or
|
||||
implied warranty. In no event will the authors be held
|
||||
liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute
|
||||
it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented;
|
||||
you must not claim that you wrote the original software.
|
||||
If you use this software in a product, an acknowledgment
|
||||
in the product documentation would be appreciated but
|
||||
is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such,
|
||||
and must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any
|
||||
source distribution.
|
||||
*********************************************************************/
|
||||
|
||||
#ifdef USE_EXTLIBS
|
||||
#include <pugixml.hpp>
|
||||
#else
|
||||
#include "detail/pugixml.hpp"
|
||||
#endif
|
||||
#include <tmxlite/FreeFuncs.hpp>
|
||||
#include <tmxlite/ObjectGroup.hpp>
|
||||
#include <tmxlite/detail/Log.hpp>
|
||||
|
||||
using namespace tmx;
|
||||
|
||||
ObjectGroup::ObjectGroup()
|
||||
: m_colour (127, 127, 127, 255),
|
||||
m_drawOrder (DrawOrder::TopDown)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//public
|
||||
void ObjectGroup::parse(const pugi::xml_node& node, Map* map)
|
||||
{
|
||||
assert(map);
|
||||
|
||||
std::string attribString = node.name();
|
||||
if (attribString != "objectgroup")
|
||||
{
|
||||
Logger::log("Node was not an object group, node will be skipped.", Logger::Type::Error);
|
||||
return;
|
||||
}
|
||||
|
||||
setName(node.attribute("name").as_string());
|
||||
setClass(node.attribute("class").as_string());
|
||||
|
||||
attribString = node.attribute("color").as_string();
|
||||
if (!attribString.empty())
|
||||
{
|
||||
m_colour = colourFromString(attribString);
|
||||
}
|
||||
|
||||
setOpacity(node.attribute("opacity").as_float(1.f));
|
||||
setVisible(node.attribute("visible").as_bool(true));
|
||||
setOffset(node.attribute("offsetx").as_int(0), node.attribute("offsety").as_int(0));
|
||||
setSize(node.attribute("width").as_uint(0), node.attribute("height").as_uint(0));
|
||||
setParallaxFactor(node.attribute("parallaxx").as_float(1.f), node.attribute("parallaxy").as_float(1.f));
|
||||
|
||||
std::string tintColour = node.attribute("tintcolor").as_string();
|
||||
if (!tintColour.empty())
|
||||
{
|
||||
setTintColour(colourFromString(tintColour));
|
||||
}
|
||||
|
||||
attribString = node.attribute("draworder").as_string();
|
||||
if (attribString == "index")
|
||||
{
|
||||
m_drawOrder = DrawOrder::Index;
|
||||
}
|
||||
|
||||
for (const auto& child : node.children())
|
||||
{
|
||||
attribString = child.name();
|
||||
if (attribString == "properties")
|
||||
{
|
||||
for (const auto& p : child)
|
||||
{
|
||||
m_properties.emplace_back();
|
||||
m_properties.back().parse(p);
|
||||
}
|
||||
}
|
||||
else if (attribString == "object")
|
||||
{
|
||||
m_objects.emplace_back();
|
||||
m_objects.back().parse(child, map);
|
||||
}
|
||||
}
|
||||
}
|
||||
154
ext/tmxlite/src/ObjectTypes.cpp
Normal file
154
ext/tmxlite/src/ObjectTypes.cpp
Normal file
@@ -0,0 +1,154 @@
|
||||
/*********************************************************************
|
||||
Raphaël Frantz 2021
|
||||
|
||||
tmxlite - Zlib license.
|
||||
|
||||
This software is provided 'as-is', without any express or
|
||||
implied warranty. In no event will the authors be held
|
||||
liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute
|
||||
it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented;
|
||||
you must not claim that you wrote the original software.
|
||||
If you use this software in a product, an acknowledgment
|
||||
in the product documentation would be appreciated but
|
||||
is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such,
|
||||
and must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any
|
||||
source distribution.
|
||||
*********************************************************************/
|
||||
|
||||
#ifdef USE_EXTLIBS
|
||||
#include <pugixml.hpp>
|
||||
#else
|
||||
#include "detail/pugixml.hpp"
|
||||
#endif
|
||||
#include <tmxlite/FreeFuncs.hpp>
|
||||
#include <tmxlite/ObjectTypes.hpp>
|
||||
#include <tmxlite/detail/Log.hpp>
|
||||
|
||||
using namespace tmx;
|
||||
|
||||
bool ObjectTypes::load(const std::string &path)
|
||||
{
|
||||
reset();
|
||||
|
||||
//open the doc
|
||||
pugi::xml_document doc;
|
||||
auto result = doc.load_file(path.c_str());
|
||||
if (!result)
|
||||
{
|
||||
Logger::log("Failed opening " + path, Logger::Type::Error);
|
||||
Logger::log("Reason: " + std::string(result.description()), Logger::Type::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
//make sure we have consistent path separators
|
||||
m_workingDirectory = path;
|
||||
std::replace(m_workingDirectory.begin(), m_workingDirectory.end(), '\\', '/');
|
||||
m_workingDirectory = getFilePath(m_workingDirectory);
|
||||
|
||||
if (!m_workingDirectory.empty() &&
|
||||
m_workingDirectory.back() == '/')
|
||||
{
|
||||
m_workingDirectory.pop_back();
|
||||
}
|
||||
|
||||
|
||||
//find the node and bail if it doesn't exist
|
||||
auto node = doc.child("objecttypes");
|
||||
if (!node)
|
||||
{
|
||||
Logger::log("Failed opening object types: " + path + ", no objecttype node found", Logger::Type::Error);
|
||||
return reset();
|
||||
}
|
||||
|
||||
return parseObjectTypesNode(node);
|
||||
}
|
||||
|
||||
bool ObjectTypes::loadFromString(const std::string &data, const std::string &workingDir)
|
||||
{
|
||||
reset();
|
||||
|
||||
//open the doc
|
||||
pugi::xml_document doc;
|
||||
auto result = doc.load_string(data.c_str());
|
||||
if (!result)
|
||||
{
|
||||
Logger::log("Failed opening object types", Logger::Type::Error);
|
||||
Logger::log("Reason: " + std::string(result.description()), Logger::Type::Error);
|
||||
return false;
|
||||
}
|
||||
|
||||
//make sure we have consistent path separators
|
||||
m_workingDirectory = workingDir;
|
||||
std::replace(m_workingDirectory.begin(), m_workingDirectory.end(), '\\', '/');
|
||||
m_workingDirectory = getFilePath(m_workingDirectory);
|
||||
|
||||
if (!m_workingDirectory.empty() &&
|
||||
m_workingDirectory.back() == '/')
|
||||
{
|
||||
m_workingDirectory.pop_back();
|
||||
}
|
||||
|
||||
|
||||
//find the node and bail if it doesn't exist
|
||||
auto node = doc.child("objecttypes");
|
||||
if (!node)
|
||||
{
|
||||
Logger::log("Failed object types: no objecttypes node found", Logger::Type::Error);
|
||||
return reset();
|
||||
}
|
||||
|
||||
return parseObjectTypesNode(node);
|
||||
}
|
||||
|
||||
bool ObjectTypes::parseObjectTypesNode(const pugi::xml_node &node)
|
||||
{
|
||||
//<objecttypes> <-- node
|
||||
// <objecttype name="Character" color="#1e47ff">
|
||||
// <property>...
|
||||
|
||||
//parse types
|
||||
for(const auto& child : node.children())
|
||||
{
|
||||
std::string attribString = child.name();
|
||||
if (attribString == "objecttype")
|
||||
{
|
||||
Type type;
|
||||
|
||||
//parse the metadata of the type
|
||||
type.name = child.attribute("name").as_string();
|
||||
type.colour = colourFromString(child.attribute("color").as_string("#FFFFFFFF"));;
|
||||
|
||||
//parse the default properties of the type
|
||||
for (const auto& p : child.children())
|
||||
{
|
||||
Property prop;
|
||||
prop.parse(p, true);
|
||||
type.properties.push_back(prop);
|
||||
}
|
||||
|
||||
m_types.push_back(type);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOG("Unidentified name " + attribString + ": node skipped", Logger::Type::Warning);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ObjectTypes::reset()
|
||||
{
|
||||
m_workingDirectory.clear();
|
||||
m_types.clear();
|
||||
return false;
|
||||
}
|
||||
167
ext/tmxlite/src/Property.cpp
Normal file
167
ext/tmxlite/src/Property.cpp
Normal file
@@ -0,0 +1,167 @@
|
||||
/*********************************************************************
|
||||
Matt Marchant 2016 - 2021
|
||||
http://trederia.blogspot.com
|
||||
|
||||
tmxlite - Zlib license.
|
||||
|
||||
This software is provided 'as-is', without any express or
|
||||
implied warranty. In no event will the authors be held
|
||||
liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute
|
||||
it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented;
|
||||
you must not claim that you wrote the original software.
|
||||
If you use this software in a product, an acknowledgment
|
||||
in the product documentation would be appreciated but
|
||||
is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such,
|
||||
and must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any
|
||||
source distribution.
|
||||
*********************************************************************/
|
||||
|
||||
#ifdef USE_EXTLIBS
|
||||
#include <pugixml.hpp>
|
||||
#else
|
||||
#include "detail/pugixml.hpp"
|
||||
#endif
|
||||
#include <tmxlite/Property.hpp>
|
||||
#include <tmxlite/detail/Log.hpp>
|
||||
#include <tmxlite/FreeFuncs.hpp>
|
||||
|
||||
using namespace tmx;
|
||||
|
||||
Property::Property()
|
||||
: m_type(Type::Undef)
|
||||
{
|
||||
}
|
||||
|
||||
Property Property::fromBoolean(bool value)
|
||||
{
|
||||
Property p;
|
||||
p.m_type = Type::Boolean;
|
||||
p.m_boolValue = value;
|
||||
return p;
|
||||
}
|
||||
|
||||
Property Property::fromFloat(float value)
|
||||
{
|
||||
Property p;
|
||||
p.m_type = Type::Float;
|
||||
p.m_floatValue = value;
|
||||
return p;
|
||||
}
|
||||
|
||||
Property Property::fromInt(int value)
|
||||
{
|
||||
Property p;
|
||||
p.m_type = Type::Int;
|
||||
p.m_intValue = value;
|
||||
return p;
|
||||
}
|
||||
|
||||
Property Property::fromString(const std::string& value)
|
||||
{
|
||||
Property p;
|
||||
p.m_type = Type::String;
|
||||
p.m_stringValue = value;
|
||||
return p;
|
||||
}
|
||||
|
||||
Property Property::fromColour(const Colour& value)
|
||||
{
|
||||
Property p;
|
||||
p.m_type = Type::Colour;
|
||||
p.m_colourValue = value;
|
||||
return p;
|
||||
}
|
||||
|
||||
Property Property::fromFile(const std::string& value)
|
||||
{
|
||||
Property p;
|
||||
p.m_type = Type::File;
|
||||
p.m_stringValue = value;
|
||||
return p;
|
||||
}
|
||||
|
||||
Property Property::fromObject(int value)
|
||||
{
|
||||
Property p;
|
||||
p.m_type = Type::Object;
|
||||
p.m_intValue = value;
|
||||
return p;
|
||||
}
|
||||
|
||||
//public
|
||||
void Property::parse(const pugi::xml_node& node, bool isObjectTypes)
|
||||
{
|
||||
// The value attribute name is different in object types
|
||||
const char *const valueAttribute = isObjectTypes ? "default" : "value";
|
||||
|
||||
std::string attribData = node.name();
|
||||
if (attribData != "property")
|
||||
{
|
||||
Logger::log("Node was not a valid property, node will be skipped", Logger::Type::Error);
|
||||
return;
|
||||
}
|
||||
|
||||
m_name = node.attribute("name").as_string();
|
||||
|
||||
attribData = node.attribute("type").as_string("string");
|
||||
if (attribData == "bool")
|
||||
{
|
||||
attribData = node.attribute(valueAttribute).as_string("false");
|
||||
m_boolValue = (attribData == "true");
|
||||
m_type = Type::Boolean;
|
||||
return;
|
||||
}
|
||||
else if (attribData == "int")
|
||||
{
|
||||
m_intValue = node.attribute(valueAttribute).as_int(0);
|
||||
m_type = Type::Int;
|
||||
return;
|
||||
}
|
||||
else if (attribData == "float")
|
||||
{
|
||||
m_floatValue = node.attribute(valueAttribute).as_float(0.f);
|
||||
m_type = Type::Float;
|
||||
return;
|
||||
}
|
||||
else if (attribData == "string")
|
||||
{
|
||||
m_stringValue = node.attribute(valueAttribute).as_string();
|
||||
|
||||
//if value is empty, try getting the child value instead
|
||||
//as this is how multiline string properties are stored.
|
||||
if(m_stringValue.empty())
|
||||
{
|
||||
m_stringValue = node.child_value();
|
||||
}
|
||||
|
||||
m_type = Type::String;
|
||||
return;
|
||||
}
|
||||
else if (attribData == "color")
|
||||
{
|
||||
m_colourValue = colourFromString(node.attribute(valueAttribute).as_string("#FFFFFFFF"));
|
||||
m_type = Type::Colour;
|
||||
return;
|
||||
}
|
||||
else if (attribData == "file")
|
||||
{
|
||||
m_stringValue = node.attribute(valueAttribute).as_string();
|
||||
m_type = Type::File;
|
||||
return;
|
||||
}
|
||||
else if (attribData == "object")
|
||||
{
|
||||
m_intValue = node.attribute(valueAttribute).as_int(0);
|
||||
m_type = Type::Object;
|
||||
return;
|
||||
}
|
||||
}
|
||||
340
ext/tmxlite/src/TileLayer.cpp
Normal file
340
ext/tmxlite/src/TileLayer.cpp
Normal file
@@ -0,0 +1,340 @@
|
||||
/*********************************************************************
|
||||
Matt Marchant 2016 - 2023
|
||||
http://trederia.blogspot.com
|
||||
|
||||
tmxlite - Zlib license.
|
||||
|
||||
This software is provided 'as-is', without any express or
|
||||
implied warranty. In no event will the authors be held
|
||||
liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute
|
||||
it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented;
|
||||
you must not claim that you wrote the original software.
|
||||
If you use this software in a product, an acknowledgment
|
||||
in the product documentation would be appreciated but
|
||||
is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such,
|
||||
and must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any
|
||||
source distribution.
|
||||
*********************************************************************/
|
||||
|
||||
#ifdef USE_EXTLIBS
|
||||
#include <pugixml.hpp>
|
||||
#include <zstd.h>
|
||||
#else
|
||||
#include "detail/pugixml.hpp"
|
||||
#endif
|
||||
|
||||
#ifdef USE_ZSTD
|
||||
#include <zstd.h>
|
||||
#endif
|
||||
|
||||
#include <tmxlite/FreeFuncs.hpp>
|
||||
#include <tmxlite/TileLayer.hpp>
|
||||
#include <tmxlite/detail/Log.hpp>
|
||||
|
||||
#include <sstream>
|
||||
|
||||
using namespace tmx;
|
||||
|
||||
namespace
|
||||
{
|
||||
struct CompressionType final
|
||||
{
|
||||
enum
|
||||
{
|
||||
Zlib, GZip, Zstd, None
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
TileLayer::TileLayer(std::size_t tileCount)
|
||||
: m_tileCount (tileCount)
|
||||
{
|
||||
m_tiles.reserve(tileCount);
|
||||
}
|
||||
|
||||
//public
|
||||
void TileLayer::parse(const pugi::xml_node& node, Map*)
|
||||
{
|
||||
std::string attribName = node.name();
|
||||
if (attribName != "layer")
|
||||
{
|
||||
Logger::log("node not a layer node, skipped parsing", Logger::Type::Error);
|
||||
return;
|
||||
}
|
||||
|
||||
setName(node.attribute("name").as_string());
|
||||
setClass(node.attribute("class").as_string());
|
||||
setOpacity(node.attribute("opacity").as_float(1.f));
|
||||
setVisible(node.attribute("visible").as_bool(true));
|
||||
setOffset(node.attribute("offsetx").as_int(0), node.attribute("offsety").as_int(0));
|
||||
setSize(node.attribute("width").as_uint(0), node.attribute("height").as_uint(0));
|
||||
setParallaxFactor(node.attribute("parallaxx").as_float(1.f), node.attribute("parallaxy").as_float(1.f));
|
||||
|
||||
std::string tintColour = node.attribute("tintcolor").as_string();
|
||||
if (!tintColour.empty())
|
||||
{
|
||||
setTintColour(colourFromString(tintColour));
|
||||
}
|
||||
|
||||
for (const auto& child : node.children())
|
||||
{
|
||||
attribName = child.name();
|
||||
if (attribName == "data")
|
||||
{
|
||||
attribName = child.attribute("encoding").as_string();
|
||||
if (attribName == "base64")
|
||||
{
|
||||
parseBase64(child);
|
||||
}
|
||||
else if (attribName == "csv")
|
||||
{
|
||||
parseCSV(child);
|
||||
}
|
||||
else
|
||||
{
|
||||
parseUnencoded(child);
|
||||
}
|
||||
}
|
||||
else if (attribName == "properties")
|
||||
{
|
||||
for (const auto& p : child.children())
|
||||
{
|
||||
addProperty(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
//private
|
||||
void TileLayer::parseBase64(const pugi::xml_node& node)
|
||||
{
|
||||
auto processDataString = [](std::string dataString, std::size_t tileCount, std::int32_t compressionType)->std::vector<std::uint32_t>
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << dataString;
|
||||
ss >> dataString;
|
||||
dataString = base64_decode(dataString);
|
||||
|
||||
std::size_t expectedSize = tileCount * 4; //4 bytes per tile
|
||||
std::vector<unsigned char> byteData;
|
||||
byteData.reserve(expectedSize);
|
||||
|
||||
switch (compressionType)
|
||||
{
|
||||
default:
|
||||
byteData.insert(byteData.end(), dataString.begin(), dataString.end());
|
||||
break;
|
||||
case CompressionType::Zstd:
|
||||
#if defined USE_ZSTD || defined USE_EXTLIBS
|
||||
{
|
||||
std::size_t dataSize = dataString.length() * sizeof(unsigned char);
|
||||
std::size_t result = ZSTD_decompress(byteData.data(), expectedSize, &dataString[0], dataSize);
|
||||
|
||||
if (ZSTD_isError(result))
|
||||
{
|
||||
std::string err = ZSTD_getErrorName(result);
|
||||
LOG("Failed to decompress layer data, node skipped.\nError: " + err, Logger::Type::Error);
|
||||
}
|
||||
}
|
||||
#else
|
||||
Logger::log("Library must be built with USE_EXTLIBS or USE_ZSTD for Zstd compression", Logger::Type::Error);
|
||||
return {};
|
||||
#endif
|
||||
case CompressionType::GZip:
|
||||
#ifndef USE_EXTLIBS
|
||||
Logger::log("Library must be built with USE_EXTLIBS for GZip compression", Logger::Type::Error);
|
||||
return {};
|
||||
#endif
|
||||
//[[fallthrough]];
|
||||
case CompressionType::Zlib:
|
||||
{
|
||||
//unzip
|
||||
std::size_t dataSize = dataString.length() * sizeof(unsigned char);
|
||||
|
||||
if (!decompress(dataString.c_str(), byteData, dataSize, expectedSize))
|
||||
{
|
||||
LOG("Failed to decompress layer data, node skipped.", Logger::Type::Error);
|
||||
return {};
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
//data stream is in bytes so we need to OR into 32 bit values
|
||||
std::vector<std::uint32_t> IDs;
|
||||
IDs.reserve(tileCount);
|
||||
for (auto i = 0u; i < expectedSize - 3u; i += 4u)
|
||||
{
|
||||
std::uint32_t id = byteData[i] | byteData[i + 1] << 8 | byteData[i + 2] << 16 | byteData[i + 3] << 24;
|
||||
IDs.push_back(id);
|
||||
}
|
||||
|
||||
return IDs;
|
||||
};
|
||||
|
||||
std::int32_t compressionType = CompressionType::None;
|
||||
std::string compression = node.attribute("compression").as_string();
|
||||
if (compression == "gzip")
|
||||
{
|
||||
compressionType = CompressionType::GZip;
|
||||
}
|
||||
else if (compression == "zlib")
|
||||
{
|
||||
compressionType = CompressionType::Zlib;
|
||||
}
|
||||
else if (compression == "zstd")
|
||||
{
|
||||
compressionType = CompressionType::Zstd;
|
||||
}
|
||||
|
||||
std::string data = node.text().as_string();
|
||||
if (data.empty())
|
||||
{
|
||||
//check for chunk nodes
|
||||
auto dataCount = 0;
|
||||
for (const auto& childNode : node.children())
|
||||
{
|
||||
std::string childName = childNode.name();
|
||||
if (childName == "chunk")
|
||||
{
|
||||
std::string dataString = childNode.text().as_string();
|
||||
if (!dataString.empty())
|
||||
{
|
||||
Chunk chunk;
|
||||
chunk.position.x = childNode.attribute("x").as_int();
|
||||
chunk.position.y = childNode.attribute("y").as_int();
|
||||
|
||||
chunk.size.x = childNode.attribute("width").as_int();
|
||||
chunk.size.y = childNode.attribute("height").as_int();
|
||||
|
||||
auto IDs = processDataString(dataString, (chunk.size.x * chunk.size.y), compressionType);
|
||||
|
||||
if (!IDs.empty())
|
||||
{
|
||||
createTiles(IDs, chunk.tiles);
|
||||
m_chunks.push_back(chunk);
|
||||
dataCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dataCount == 0)
|
||||
{
|
||||
Logger::log("Layer " + getName() + " has no layer data. Layer skipped.", Logger::Type::Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
auto IDs = processDataString(data, m_tileCount, compressionType);
|
||||
createTiles(IDs, m_tiles);
|
||||
}
|
||||
}
|
||||
|
||||
void TileLayer::parseCSV(const pugi::xml_node& node)
|
||||
{
|
||||
auto processDataString = [](const std::string dataString, std::size_t tileCount)->std::vector<std::uint32_t>
|
||||
{
|
||||
std::vector<std::uint32_t> IDs;
|
||||
IDs.reserve(tileCount);
|
||||
|
||||
const char* ptr = dataString.c_str();
|
||||
while (true)
|
||||
{
|
||||
char* end;
|
||||
auto res = std::strtoul(ptr, &end, 10);
|
||||
if (end == ptr) break;
|
||||
ptr = end;
|
||||
IDs.push_back(res);
|
||||
if (*ptr == ',') ++ptr;
|
||||
}
|
||||
|
||||
return IDs;
|
||||
};
|
||||
|
||||
std::string data = node.text().as_string();
|
||||
if (data.empty())
|
||||
{
|
||||
//check for chunk nodes
|
||||
auto dataCount = 0;
|
||||
for (const auto& childNode : node.children())
|
||||
{
|
||||
std::string childName = childNode.name();
|
||||
if (childName == "chunk")
|
||||
{
|
||||
std::string dataString = childNode.text().as_string();
|
||||
if (!dataString.empty())
|
||||
{
|
||||
Chunk chunk;
|
||||
chunk.position.x = childNode.attribute("x").as_int();
|
||||
chunk.position.y = childNode.attribute("y").as_int();
|
||||
|
||||
chunk.size.x = childNode.attribute("width").as_int();
|
||||
chunk.size.y = childNode.attribute("height").as_int();
|
||||
|
||||
auto IDs = processDataString(dataString, chunk.size.x * chunk.size.y);
|
||||
|
||||
if (!IDs.empty())
|
||||
{
|
||||
createTiles(IDs, chunk.tiles);
|
||||
m_chunks.push_back(chunk);
|
||||
dataCount++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (dataCount == 0)
|
||||
{
|
||||
Logger::log("Layer " + getName() + " has no layer data. Layer skipped.", Logger::Type::Error);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
createTiles(processDataString(data, m_tileCount), m_tiles);
|
||||
}
|
||||
}
|
||||
|
||||
void TileLayer::parseUnencoded(const pugi::xml_node& node)
|
||||
{
|
||||
std::string attribName;
|
||||
std::vector<std::uint32_t> IDs;
|
||||
IDs.reserve(m_tileCount);
|
||||
|
||||
for (const auto& child : node.children())
|
||||
{
|
||||
attribName = child.name();
|
||||
if (attribName == "tile")
|
||||
{
|
||||
IDs.push_back(child.attribute("gid").as_uint());
|
||||
}
|
||||
}
|
||||
|
||||
createTiles(IDs, m_tiles);
|
||||
}
|
||||
|
||||
void TileLayer::createTiles(const std::vector<std::uint32_t>& IDs, std::vector<Tile>& destination)
|
||||
{
|
||||
//LOG(IDs.size() != m_tileCount, "Layer tile count does not match expected size. Found: "
|
||||
// + std::to_string(IDs.size()) + ", expected: " + std::to_string(m_tileCount));
|
||||
|
||||
static const std::uint32_t mask = 0xf0000000;
|
||||
for (const auto& id : IDs)
|
||||
{
|
||||
destination.emplace_back();
|
||||
destination.back().flipFlags = ((id & mask) >> 28);
|
||||
destination.back().ID = id & ~mask;
|
||||
}
|
||||
}
|
||||
460
ext/tmxlite/src/Tileset.cpp
Normal file
460
ext/tmxlite/src/Tileset.cpp
Normal file
@@ -0,0 +1,460 @@
|
||||
/*********************************************************************
|
||||
Matt Marchant 2016 - 2023
|
||||
http://trederia.blogspot.com
|
||||
|
||||
tmxlite - Zlib license.
|
||||
|
||||
This software is provided 'as-is', without any express or
|
||||
implied warranty. In no event will the authors be held
|
||||
liable for any damages arising from the use of this software.
|
||||
|
||||
Permission is granted to anyone to use this software for any purpose,
|
||||
including commercial applications, and to alter it and redistribute
|
||||
it freely, subject to the following restrictions:
|
||||
|
||||
1. The origin of this software must not be misrepresented;
|
||||
you must not claim that you wrote the original software.
|
||||
If you use this software in a product, an acknowledgment
|
||||
in the product documentation would be appreciated but
|
||||
is not required.
|
||||
|
||||
2. Altered source versions must be plainly marked as such,
|
||||
and must not be misrepresented as being the original software.
|
||||
|
||||
3. This notice may not be removed or altered from any
|
||||
source distribution.
|
||||
*********************************************************************/
|
||||
|
||||
#ifdef USE_EXTLIBS
|
||||
#include <pugixml.hpp>
|
||||
#else
|
||||
#include "detail/pugixml.hpp"
|
||||
#endif
|
||||
#include <tmxlite/Tileset.hpp>
|
||||
#include <tmxlite/FreeFuncs.hpp>
|
||||
#include <tmxlite/detail/Log.hpp>
|
||||
|
||||
#include <ctype.h>
|
||||
|
||||
using namespace tmx;
|
||||
|
||||
Tileset::Tileset(const std::string& workingDir)
|
||||
: m_workingDir (workingDir),
|
||||
m_firstGID (0),
|
||||
m_spacing (0),
|
||||
m_margin (0),
|
||||
m_tileCount (0),
|
||||
m_columnCount (0),
|
||||
m_objectAlignment (ObjectAlignment::Unspecified),
|
||||
m_transparencyColour (0, 0, 0, 0),
|
||||
m_hasTransparency (false)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
//public
|
||||
void Tileset::parse(pugi::xml_node node, Map* map)
|
||||
{
|
||||
assert(map);
|
||||
|
||||
std::string attribString = node.name();
|
||||
if (attribString != "tileset")
|
||||
{
|
||||
Logger::log(attribString + ": not a tileset node! Node will be skipped.", Logger::Type::Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
m_firstGID = node.attribute("firstgid").as_int();
|
||||
if (m_firstGID == 0)
|
||||
{
|
||||
Logger::log("Invalid first GID in tileset. Tileset node skipped.", Logger::Type::Warning);
|
||||
return;
|
||||
}
|
||||
|
||||
pugi::xml_document tsxDoc; //need to keep this in scope
|
||||
if (node.attribute("source"))
|
||||
{
|
||||
//parse TSX doc
|
||||
std::string path = node.attribute("source").as_string();
|
||||
path = resolveFilePath(path, m_workingDir);
|
||||
|
||||
//as the TSX file now dictates the image path, the working
|
||||
//directory is now that of the tsx file
|
||||
auto position = path.find_last_of('/');
|
||||
if (position != std::string::npos)
|
||||
{
|
||||
m_workingDir = path.substr(0, position);
|
||||
}
|
||||
else
|
||||
{
|
||||
m_workingDir = "";
|
||||
}
|
||||
|
||||
//see if doc can be opened
|
||||
auto result = tsxDoc.load_file(path.c_str());
|
||||
if (!result)
|
||||
{
|
||||
Logger::log(path + ": Failed opening tsx file for tile set, tile set will be skipped", Logger::Type::Error);
|
||||
return reset();
|
||||
}
|
||||
|
||||
//if it can then replace the current node with tsx node
|
||||
node = tsxDoc.child("tileset");
|
||||
if (!node)
|
||||
{
|
||||
Logger::log("tsx file does not contain a tile set node, tile set will be skipped", Logger::Type::Error);
|
||||
return reset();
|
||||
}
|
||||
}
|
||||
|
||||
m_name = node.attribute("name").as_string();
|
||||
LOG("found tile set " + m_name, Logger::Type::Info);
|
||||
m_class = node.attribute("class").as_string();
|
||||
|
||||
m_tileSize.x = node.attribute("tilewidth").as_int();
|
||||
m_tileSize.y = node.attribute("tileheight").as_int();
|
||||
if (m_tileSize.x == 0 || m_tileSize.y == 0)
|
||||
{
|
||||
Logger::log("Invalid tile size found in tile set node. Node will be skipped.", Logger::Type::Error);
|
||||
return reset();
|
||||
}
|
||||
|
||||
m_spacing = node.attribute("spacing").as_int();
|
||||
m_margin = node.attribute("margin").as_int();
|
||||
m_tileCount = node.attribute("tilecount").as_int();
|
||||
m_columnCount = node.attribute("columns").as_int();
|
||||
|
||||
m_tileIndex.reserve(m_tileCount);
|
||||
m_tiles.reserve(m_tileCount);
|
||||
|
||||
std::string objectAlignment = node.attribute("objectalignment").as_string();
|
||||
if (!objectAlignment.empty())
|
||||
{
|
||||
if (objectAlignment == "unspecified")
|
||||
{
|
||||
m_objectAlignment = ObjectAlignment::Unspecified;
|
||||
}
|
||||
else if (objectAlignment == "topleft")
|
||||
{
|
||||
m_objectAlignment = ObjectAlignment::TopLeft;
|
||||
}
|
||||
else if (objectAlignment == "top")
|
||||
{
|
||||
m_objectAlignment = ObjectAlignment::Top;
|
||||
}
|
||||
else if (objectAlignment == "topright")
|
||||
{
|
||||
m_objectAlignment = ObjectAlignment::TopRight;
|
||||
}
|
||||
else if (objectAlignment == "left")
|
||||
{
|
||||
m_objectAlignment = ObjectAlignment::Left;
|
||||
}
|
||||
else if (objectAlignment == "center")
|
||||
{
|
||||
m_objectAlignment = ObjectAlignment::Center;
|
||||
}
|
||||
else if (objectAlignment == "right")
|
||||
{
|
||||
m_objectAlignment = ObjectAlignment::Right;
|
||||
}
|
||||
else if (objectAlignment == "bottomleft")
|
||||
{
|
||||
m_objectAlignment = ObjectAlignment::BottomLeft;
|
||||
}
|
||||
else if (objectAlignment == "bottom")
|
||||
{
|
||||
m_objectAlignment = ObjectAlignment::Bottom;
|
||||
}
|
||||
else if (objectAlignment == "bottomright")
|
||||
{
|
||||
m_objectAlignment = ObjectAlignment::BottomRight;
|
||||
}
|
||||
}
|
||||
|
||||
const auto& children = node.children();
|
||||
for (const auto& node : children)
|
||||
{
|
||||
std::string name = node.name();
|
||||
if (name == "image")
|
||||
{
|
||||
//TODO this currently doesn't cover embedded images
|
||||
//mostly because I can't figure out how to export them
|
||||
//from the Tiled editor... but also resource handling
|
||||
//should be handled by the renderer, not the parser.
|
||||
attribString = node.attribute("source").as_string();
|
||||
if (attribString.empty())
|
||||
{
|
||||
Logger::log("Tileset image node has missing source property, tile set not loaded", Logger::Type::Error);
|
||||
return reset();
|
||||
}
|
||||
m_imagePath = resolveFilePath(attribString, m_workingDir);
|
||||
if (node.attribute("trans"))
|
||||
{
|
||||
attribString = node.attribute("trans").as_string();
|
||||
m_transparencyColour = colourFromString(attribString);
|
||||
m_hasTransparency = true;
|
||||
}
|
||||
if (node.attribute("width") && node.attribute("height"))
|
||||
{
|
||||
m_imageSize.x = node.attribute("width").as_int();
|
||||
m_imageSize.y = node.attribute("height").as_int();
|
||||
}
|
||||
}
|
||||
else if (name == "tileoffset")
|
||||
{
|
||||
parseOffsetNode(node);
|
||||
}
|
||||
else if (name == "properties")
|
||||
{
|
||||
parsePropertyNode(node);
|
||||
}
|
||||
else if (name == "terraintypes")
|
||||
{
|
||||
parseTerrainNode(node);
|
||||
}
|
||||
else if (name == "tile")
|
||||
{
|
||||
parseTileNode(node, map);
|
||||
}
|
||||
}
|
||||
|
||||
//if the tsx file does not declare every tile, we create the missing ones
|
||||
if (m_tiles.size() != getTileCount())
|
||||
{
|
||||
for (std::uint32_t ID = 0; ID < getTileCount(); ID++)
|
||||
{
|
||||
createMissingTile(ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::uint32_t Tileset::getLastGID() const
|
||||
{
|
||||
assert(!m_tileIndex.empty());
|
||||
return m_firstGID + static_cast<std::uint32_t>(m_tileIndex.size()) - 1;
|
||||
}
|
||||
|
||||
const Tileset::Tile* Tileset::getTile(std::uint32_t id) const
|
||||
{
|
||||
if (!hasTile(id))
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
//corrects the ID. Indices and IDs are different.
|
||||
id -= m_firstGID;
|
||||
id = m_tileIndex[id];
|
||||
return id ? &m_tiles[id - 1] : nullptr;
|
||||
}
|
||||
|
||||
//private
|
||||
void Tileset::reset()
|
||||
{
|
||||
m_firstGID = 0;
|
||||
m_source = "";
|
||||
m_name = "";
|
||||
m_class = "";
|
||||
m_tileSize = { 0,0 };
|
||||
m_spacing = 0;
|
||||
m_margin = 0;
|
||||
m_tileCount = 0;
|
||||
m_columnCount = 0;
|
||||
m_objectAlignment = ObjectAlignment::Unspecified;
|
||||
m_tileOffset = { 0,0 };
|
||||
m_properties.clear();
|
||||
m_imagePath = "";
|
||||
m_transparencyColour = { 0, 0, 0, 0 };
|
||||
m_hasTransparency = false;
|
||||
m_terrainTypes.clear();
|
||||
m_tileIndex.clear();
|
||||
m_tiles.clear();
|
||||
}
|
||||
|
||||
void Tileset::parseOffsetNode(const pugi::xml_node& node)
|
||||
{
|
||||
m_tileOffset.x = node.attribute("x").as_int();
|
||||
m_tileOffset.y = node.attribute("y").as_int();
|
||||
}
|
||||
|
||||
void Tileset::parsePropertyNode(const pugi::xml_node& node)
|
||||
{
|
||||
const auto& children = node.children();
|
||||
for (const auto& child : children)
|
||||
{
|
||||
m_properties.emplace_back();
|
||||
m_properties.back().parse(child);
|
||||
}
|
||||
}
|
||||
|
||||
void Tileset::parseTerrainNode(const pugi::xml_node& node)
|
||||
{
|
||||
const auto& children = node.children();
|
||||
for (const auto& child : children)
|
||||
{
|
||||
std::string name = child.name();
|
||||
if (name == "terrain")
|
||||
{
|
||||
m_terrainTypes.emplace_back();
|
||||
auto& terrain = m_terrainTypes.back();
|
||||
terrain.name = child.attribute("name").as_string();
|
||||
terrain.tileID = child.attribute("tile").as_int();
|
||||
auto properties = child.child("properties");
|
||||
if (properties)
|
||||
{
|
||||
for (const auto& p : properties)
|
||||
{
|
||||
name = p.name();
|
||||
if (name == "property")
|
||||
{
|
||||
terrain.properties.emplace_back();
|
||||
terrain.properties.back().parse(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Tileset::Tile& Tileset::newTile(std::uint32_t ID)
|
||||
{
|
||||
Tile& tile = (m_tiles.emplace_back(), m_tiles.back());
|
||||
if (m_tileIndex.size() <= ID)
|
||||
{
|
||||
m_tileIndex.resize(ID + 1, 0);
|
||||
}
|
||||
|
||||
m_tileIndex[ID] = static_cast<std::uint32_t>(m_tiles.size());
|
||||
tile.ID = ID;
|
||||
return tile;
|
||||
}
|
||||
|
||||
void Tileset::parseTileNode(const pugi::xml_node& node, Map* map)
|
||||
{
|
||||
assert(map);
|
||||
|
||||
Tile& tile = newTile(node.attribute("id").as_int());
|
||||
if (node.attribute("terrain"))
|
||||
{
|
||||
std::string data = node.attribute("terrain").as_string();
|
||||
bool lastWasChar = true;
|
||||
std::size_t idx = 0u;
|
||||
for (auto i = 0u; i < data.size() && idx < tile.terrainIndices.size(); ++i)
|
||||
{
|
||||
if (isdigit(data[i]))
|
||||
{
|
||||
tile.terrainIndices[idx++] = std::atoi(&data[i]);
|
||||
lastWasChar = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!lastWasChar)
|
||||
{
|
||||
lastWasChar = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
tile.terrainIndices[idx++] = -1;
|
||||
lastWasChar = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (lastWasChar)
|
||||
{
|
||||
tile.terrainIndices[idx] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
tile.probability = node.attribute("probability").as_int(100);
|
||||
|
||||
tile.className = node.attribute("type").as_string();
|
||||
if (tile.className.empty())
|
||||
{
|
||||
tile.className = node.attribute("class").as_string();
|
||||
}
|
||||
|
||||
//by default we set the tile's values as in an Image tileset
|
||||
tile.imagePath = m_imagePath;
|
||||
tile.imageSize = m_tileSize;
|
||||
|
||||
if (m_columnCount != 0)
|
||||
{
|
||||
std::uint32_t rowIndex = tile.ID % m_columnCount;
|
||||
std::uint32_t columnIndex = tile.ID / m_columnCount;
|
||||
tile.imagePosition.x = m_margin + rowIndex * (m_tileSize.x + m_spacing);
|
||||
tile.imagePosition.y = m_margin + columnIndex * (m_tileSize.y + m_spacing);
|
||||
}
|
||||
|
||||
const auto& children = node.children();
|
||||
for (const auto& child : children)
|
||||
{
|
||||
std::string name = child.name();
|
||||
if (name == "properties")
|
||||
{
|
||||
for (const auto& prop : child.children())
|
||||
{
|
||||
tile.properties.emplace_back();
|
||||
tile.properties.back().parse(prop);
|
||||
}
|
||||
}
|
||||
else if (name == "objectgroup")
|
||||
{
|
||||
tile.objectGroup.parse(child, map);
|
||||
}
|
||||
else if (name == "image")
|
||||
{
|
||||
std::string attribString = child.attribute("source").as_string();
|
||||
if (attribString.empty())
|
||||
{
|
||||
Logger::log("Tile image path missing", Logger::Type::Warning);
|
||||
continue;
|
||||
}
|
||||
tile.imagePath = resolveFilePath(attribString, m_workingDir);
|
||||
|
||||
tile.imagePosition = tmx::Vector2u(0, 0);
|
||||
|
||||
if (child.attribute("trans"))
|
||||
{
|
||||
attribString = child.attribute("trans").as_string();
|
||||
m_transparencyColour = colourFromString(attribString);
|
||||
m_hasTransparency = true;
|
||||
}
|
||||
if (child.attribute("width"))
|
||||
{
|
||||
tile.imageSize.x = child.attribute("width").as_uint();
|
||||
}
|
||||
if (child.attribute("height"))
|
||||
{
|
||||
tile.imageSize.y = child.attribute("height").as_uint();
|
||||
}
|
||||
}
|
||||
else if (name == "animation")
|
||||
{
|
||||
for (const auto& frameNode : child.children())
|
||||
{
|
||||
Tile::Animation::Frame frame;
|
||||
frame.duration = frameNode.attribute("duration").as_int();
|
||||
frame.tileID = frameNode.attribute("tileid").as_int() + m_firstGID;
|
||||
tile.animation.frames.push_back(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Tileset::createMissingTile(std::uint32_t ID)
|
||||
{
|
||||
//first, we check if the tile does not yet exist
|
||||
if (m_tileIndex.size() > ID && m_tileIndex[ID])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Tile& tile = newTile(ID);
|
||||
tile.imagePath = m_imagePath;
|
||||
tile.imageSize = m_tileSize;
|
||||
|
||||
std::uint32_t rowIndex = ID % m_columnCount;
|
||||
std::uint32_t columnIndex = ID / m_columnCount;
|
||||
tile.imagePosition.x = m_margin + rowIndex * (m_tileSize.x + m_spacing);
|
||||
tile.imagePosition.y = m_margin + columnIndex * (m_tileSize.y + m_spacing);
|
||||
}
|
||||
75
ext/tmxlite/src/detail/pugiconfig.hpp
Normal file
75
ext/tmxlite/src/detail/pugiconfig.hpp
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* pugixml parser - version 1.7
|
||||
* --------------------------------------------------------
|
||||
* Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
|
||||
* Report bugs and download new versions at http://pugixml.org/
|
||||
*
|
||||
* This library is distributed under the MIT License. See notice at the end
|
||||
* of this file.
|
||||
*
|
||||
* This work is based on the pugxml parser, which is:
|
||||
* Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
|
||||
*/
|
||||
|
||||
#ifndef HEADER_PUGICONFIG_HPP
|
||||
#define HEADER_PUGICONFIG_HPP
|
||||
|
||||
// Uncomment this to enable wchar_t mode
|
||||
// #define PUGIXML_WCHAR_MODE
|
||||
|
||||
// Uncomment this to enable compact mode
|
||||
// #define PUGIXML_COMPACT
|
||||
|
||||
// Uncomment this to disable XPath
|
||||
// #define PUGIXML_NO_XPATH
|
||||
|
||||
#ifdef __ANDROID__
|
||||
// Uncomment this to disable STL
|
||||
#define PUGIXML_NO_STL
|
||||
|
||||
// Uncomment this to disable exceptions
|
||||
#define PUGIXML_NO_EXCEPTIONS
|
||||
#endif //__ANDROID__
|
||||
// Set this to control attributes for public classes/functions, i.e.:
|
||||
// #define PUGIXML_API __declspec(dllexport) // to export all public symbols from DLL
|
||||
// #define PUGIXML_CLASS __declspec(dllimport) // to import all classes from DLL
|
||||
// #define PUGIXML_FUNCTION __fastcall // to set calling conventions to all public functions to fastcall
|
||||
// In absence of PUGIXML_CLASS/PUGIXML_FUNCTION definitions PUGIXML_API is used instead
|
||||
|
||||
// Tune these constants to adjust memory-related behavior
|
||||
// #define PUGIXML_MEMORY_PAGE_SIZE 32768
|
||||
// #define PUGIXML_MEMORY_OUTPUT_STACK 10240
|
||||
// #define PUGIXML_MEMORY_XPATH_PAGE_SIZE 4096
|
||||
|
||||
// Uncomment this to switch to header-only version
|
||||
//#define PUGIXML_HEADER_ONLY
|
||||
|
||||
// Uncomment this to enable long long support
|
||||
// #define PUGIXML_HAS_LONG_LONG
|
||||
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Copyright (c) 2006-2015 Arseny Kapoulkine
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
34
ext/tmxlite/src/detail/pugixml.LICENSE
Normal file
34
ext/tmxlite/src/detail/pugixml.LICENSE
Normal file
@@ -0,0 +1,34 @@
|
||||
/**
|
||||
* pugixml parser - version 1.7
|
||||
* --------------------------------------------------------
|
||||
* Copyright (C) 2006-2015, by Arseny Kapoulkine (arseny.kapoulkine@gmail.com)
|
||||
* Report bugs and download new versions at http://pugixml.org/
|
||||
*
|
||||
* This library is distributed under the MIT License.
|
||||
*
|
||||
* This work is based on the pugxml parser, which is:
|
||||
* Copyright (C) 2003, by Kristen Wegner (kristen@tima.net)
|
||||
*
|
||||
*
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person
|
||||
* obtaining a copy of this software and associated documentation
|
||||
* files (the "Software"), to deal in the Software without
|
||||
* restriction, including without limitation the rights to use,
|
||||
* copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the
|
||||
* Software is furnished to do so, subject to the following
|
||||
* conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be
|
||||
* included in all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
||||
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
||||
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
||||
* OTHER DEALINGS IN THE SOFTWARE.
|
||||
*/
|
||||
12426
ext/tmxlite/src/detail/pugixml.cpp
Normal file
12426
ext/tmxlite/src/detail/pugixml.cpp
Normal file
File diff suppressed because it is too large
Load Diff
1400
ext/tmxlite/src/detail/pugixml.hpp
Normal file
1400
ext/tmxlite/src/detail/pugixml.hpp
Normal file
File diff suppressed because it is too large
Load Diff
59
ext/tmxlite/src/meson.build
Normal file
59
ext/tmxlite/src/meson.build
Normal file
@@ -0,0 +1,59 @@
|
||||
if get_option('use_extlibs')
|
||||
tmxlite_lib = library(meson.project_name() + binary_postfix,
|
||||
'FreeFuncs.cpp',
|
||||
'ImageLayer.cpp',
|
||||
'Map.cpp',
|
||||
'Object.cpp',
|
||||
'ObjectGroup.cpp',
|
||||
'Property.cpp',
|
||||
'TileLayer.cpp',
|
||||
'LayerGroup.cpp',
|
||||
'Tileset.cpp',
|
||||
install: true,
|
||||
include_directories: incdir,
|
||||
dependencies: [zdep, pugidep, zstddep]
|
||||
)
|
||||
else
|
||||
|
||||
if get_option('use_zstd')
|
||||
|
||||
tmxlite_lib = library(meson.project_name() + binary_postfix,
|
||||
'detail/pugixml.cpp',
|
||||
'FreeFuncs.cpp',
|
||||
'ImageLayer.cpp',
|
||||
'Map.cpp',
|
||||
'miniz.c',
|
||||
'Object.cpp',
|
||||
'ObjectGroup.cpp',
|
||||
'Property.cpp',
|
||||
'TileLayer.cpp',
|
||||
'LayerGroup.cpp',
|
||||
'Tileset.cpp',
|
||||
install: true,
|
||||
include_directories: incdir,
|
||||
dependencies: zstddep
|
||||
)
|
||||
else
|
||||
|
||||
tmxlite_lib = library(meson.project_name() + binary_postfix,
|
||||
'detail/pugixml.cpp',
|
||||
'FreeFuncs.cpp',
|
||||
'ImageLayer.cpp',
|
||||
'Map.cpp',
|
||||
'miniz.c',
|
||||
'Object.cpp',
|
||||
'ObjectGroup.cpp',
|
||||
'Property.cpp',
|
||||
'TileLayer.cpp',
|
||||
'LayerGroup.cpp',
|
||||
'Tileset.cpp',
|
||||
install: true,
|
||||
include_directories: incdir,
|
||||
)
|
||||
endif
|
||||
endif
|
||||
|
||||
tmxlite_dep = declare_dependency(
|
||||
link_with: tmxlite_lib,
|
||||
include_directories: incdir,
|
||||
)
|
||||
4916
ext/tmxlite/src/miniz.c
Normal file
4916
ext/tmxlite/src/miniz.c
Normal file
File diff suppressed because it is too large
Load Diff
8
ext/tmxlite/src/miniz.h
Normal file
8
ext/tmxlite/src/miniz.h
Normal file
@@ -0,0 +1,8 @@
|
||||
/*
|
||||
miniz public domain replacement for zlib. See miniz.c
|
||||
for more information.
|
||||
*/
|
||||
#ifndef MINIZ_HEADER_FILE_ONLY
|
||||
#define MINIZ_HEADER_FILE_ONLY
|
||||
#include "miniz.c"
|
||||
#endif //MINIZ_HEADER_FILE_ONLY
|
||||
Reference in New Issue
Block a user