From 385f7b069f390c6c8de1c2bf5002cf9b91ece2ef Mon Sep 17 00:00:00 2001 From: a dinosaur Date: Wed, 10 Apr 2024 02:31:43 +1000 Subject: [PATCH] restore build & basic functionality --- ext/base64/CMakeLists.txt | 1 + src/CMakeLists.txt | 10 +- src/tmxlayer.hpp | 27 ++--- src/tmxobject.hpp | 16 +-- src/tmxreader.cpp | 244 ++++++++++++++++++++------------------ src/tmxreader.hpp | 7 +- src/tmxtileset.hpp | 24 ++-- 7 files changed, 170 insertions(+), 159 deletions(-) diff --git a/ext/base64/CMakeLists.txt b/ext/base64/CMakeLists.txt index 8bb13a5..fce6120 100644 --- a/ext/base64/CMakeLists.txt +++ b/ext/base64/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(base64 base64.cpp base64.h) add_library(base64::base64 ALIAS base64) +set_target_properties(base64 PROPERTIES CXX_STANDARD 17) target_include_directories(base64 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 08895f0..7372822 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,5 +1,6 @@ add_executable(tmx2gba argparse.hpp argparse.cpp + $<$>:gzip.hpp gzip.cpp> tmxlayer.hpp tmxobject.hpp tmxtileset.hpp @@ -15,16 +16,21 @@ target_include_directories(tmx2gba PRIVATE ${CMAKE_CURRENT_BINARY_DIR}) set_target_properties(tmx2gba PROPERTIES CXX_STANDARD 20) +target_compile_definitions(${PROJECT_NAME} PRIVATE + $<$:_CRT_SECURE_NO_WARNINGS> # disable msvc warning + $<$:USE_ZLIB>) + # Enable strong warnings target_compile_options(tmx2gba PRIVATE $<$:/Wall> $<$:-Wall -Wextra -pedantic> $<$:-Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded>) -target_link_libraries(${PROJECT_NAME} base64::base64 pugixml Zstd::Zstd +target_link_libraries(tmx2gba External::rapidxml) + #pugixml) +target_link_libraries(tmx2gba base64::base64 Zstd::Zstd $<$:ZLIB::ZLIB> $<$:miniz::miniz>) -target_link_libraries(${PROJECT_NAME} External::rapidxml) if (TMX2GBA_DKP_INSTALL) if (DEFINED ENV{DEVKITPRO}) diff --git a/src/tmxlayer.hpp b/src/tmxlayer.hpp index 34caf34..d5504a4 100644 --- a/src/tmxlayer.hpp +++ b/src/tmxlayer.hpp @@ -1,34 +1,33 @@ -/* tmxlayer.hpp - Copyright (C) 2015-2022 a dinosaur (zlib, see COPYING.txt) */ +/* tmxlayer.hpp - Copyright (C) 2015-2024 a dinosaur (zlib, see COPYING.txt) */ #ifndef TMXLAYER_HPP #define TMXLAYER_HPP +#include +#include #include +#include #include #include class TmxLayer { + std::string mName; + int mWidth, mHeight; + std::vector mTileDat; + public: static constexpr uint32_t FLIP_HORZ = 0x80000000; static constexpr uint32_t FLIP_VERT = 0x40000000; static constexpr uint32_t FLIP_DIAG = 0x20000000; static constexpr uint32_t FLIP_MASK = 0xE0000000; - TmxLayer() : mWidth(0), mHeight(0), mTileDat(nullptr) {} - TmxLayer(int aWidth, int aHeight, std::string aName, uint32_t* aTileDat) - : mName(std::move(aName)), mWidth(aWidth), mHeight(aHeight), mTileDat(aTileDat) {} - inline ~TmxLayer() { delete[] mTileDat; } + TmxLayer(int width, int height, const std::string_view name, std::vector&& tileDat) noexcept + : mName(name), mWidth(width), mHeight(height), mTileDat(std::move(tileDat)) {} - constexpr const std::string& GetName() const { return mName; } - constexpr int GetWidth() const { return mWidth; } - constexpr int GetHeight() const { return mHeight; } - constexpr const uint32_t* GetData() const { return mTileDat; } - -private: - std::string mName; - int mWidth, mHeight; - uint32_t* mTileDat; + [[nodiscard]] constexpr const std::string_view Name() const noexcept { return mName; } + [[nodiscard]] constexpr std::pair TileCount() const noexcept { return { mWidth, mHeight }; } + [[nodiscard]] constexpr const std::span Tiles() const noexcept { return mTileDat; } }; #endif//TMXLAYER_HPP diff --git a/src/tmxobject.hpp b/src/tmxobject.hpp index 85b5b41..22cf244 100644 --- a/src/tmxobject.hpp +++ b/src/tmxobject.hpp @@ -1,4 +1,4 @@ -/* tmxobject.hpp - Copyright (C) 2015-2022 a dinosaur (zlib, see COPYING.txt) */ +/* tmxobject.hpp - Copyright (C) 2015-2024 a dinosaur (zlib, see COPYING.txt) */ #ifndef TMXOBJECT_HPP #define TMXOBJECT_HPP @@ -9,17 +9,17 @@ class TmxObject { public: - TmxObject() : mX(0.0f), mY(0.0f) {} - TmxObject(std::string aName, float aX, float aY) - : mName(std::move(aName)), mX(aX), mY(aY) {} - ~TmxObject() = default; + TmxObject(std::string_view name, float x, float y) : mName(name), mPos{ x, y } {} - constexpr const std::string& GetName() const { return mName; } - inline void GetPos(float& aOutX, float& aOutY) const { aOutX = mX; aOutY = mY; } + template + struct Position { T x, y; }; + + constexpr const std::string_view Name() const noexcept { return mName; } + constexpr Position Pos() const noexcept { return mPos; } private: std::string mName; - float mX, mY; + Position mPos; }; #endif//TMXOBJECT_HPP diff --git a/src/tmxreader.cpp b/src/tmxreader.cpp index 1c2b38d..98c1683 100644 --- a/src/tmxreader.cpp +++ b/src/tmxreader.cpp @@ -4,47 +4,74 @@ #include "tmxtileset.hpp" #include "tmxobject.hpp" #include "tmxlayer.hpp" +#include "base64.h" +#ifdef USE_ZLIB +# include +#else +# include "gzip.hpp" +#endif +#include #include #include +#include +#include +#include -bool TmxReader::DecodeMap(uint32_t* aOut, size_t aOutSize, const std::string& aBase64Dat) + +class TmxMap +{ + int mWidth = 0, mHeight = 0; + + std::vector mLayers; + std::vector mTilesets; + std::vector mObjects; + + [[nodiscard]] bool Decode(std::span out, const std::string_view base64); + void ReadTileset(rapidxml::xml_node<>* aXNode); + void ReadLayer(rapidxml::xml_node<>* aXNode); + void ReadObjects(rapidxml::xml_node<>* aXNode); + +public: + [[nodiscard]] bool Load(const std::string_view inPath); + + constexpr std::pair TileCount() const noexcept { return { mWidth, mHeight }; } + constexpr const std::vector& Tilesets() const noexcept { return mTilesets; } + constexpr const std::vector& Layers() const noexcept { return mLayers; } +}; + + +bool TmxMap::Decode(std::span out, const std::string_view base64) { // Cut leading & trailing whitespace (including newlines) - auto beg = std::find_if_not(aBase64Dat.begin(), aBase64Dat.end(), ::isspace); - if (beg == std::end(aBase64Dat)) + auto beg = std::find_if_not(base64.begin(), base64.end(), ::isspace); + if (beg == std::end(base64)) return false; - auto end = std::find_if_not(aBase64Dat.rbegin(), aBase64Dat.rend(), ::isspace); - std::size_t begOff = std::distance(aBase64Dat.begin(), beg); - std::size_t endOff = std::distance(end, aBase64Dat.rend()) - begOff; - auto trimmed = aBase64Dat.substr(begOff, endOff); + auto end = std::find_if_not(base64.rbegin(), base64.rend(), ::isspace); + std::size_t begOff = std::distance(base64.begin(), beg); + std::size_t endOff = std::distance(end, base64.rend()) - begOff; + const auto trimmed = base64.substr(begOff, endOff); // Decode base64 string std::string decoded = base64_decode(trimmed); // Decompress compressed data - auto dstSize = static_cast(aOutSize); + auto dstSize = static_cast(sizeof(uint32_t) * out.size()); int res = uncompress( - reinterpret_cast(aOut), + reinterpret_cast(out.data()), &dstSize, reinterpret_cast(decoded.data()), - static_cast(decoded.size())); - decoded.clear(); - if (res < 0) - return false; + static_cast(decoded.size())); - return true; + return res >= 0; } -void TmxReader::ReadTileset(rapidxml::xml_node<>* aXNode) +void TmxMap::ReadTileset(rapidxml::xml_node<>* aXNode) { - rapidxml::xml_attribute<>* xAttrib; - - const char* name = ""; - const char* source = ""; - uint32_t firstGid = 0; + std::string_view name, source; + uint32_t firstGid = 0, lastGid = 0; // Read name - xAttrib = aXNode->first_attribute("name"); + auto xAttrib = aXNode->first_attribute("name"); if (xAttrib != nullptr) name = xAttrib->value(); @@ -58,19 +85,21 @@ void TmxReader::ReadTileset(rapidxml::xml_node<>* aXNode) if (xAttrib != nullptr) firstGid = static_cast(std::stoul(xAttrib->value())); - mTilesets.push_back(new TmxTileset(name, source, firstGid)); + // Read last global ID + xAttrib = aXNode->first_attribute("lastgid"); + if (xAttrib) + lastGid = static_cast(std::stoul(xAttrib->value())); + + mTilesets.emplace_back(TmxTileset(name, source, firstGid, lastGid)); } -void TmxReader::ReadLayer(rapidxml::xml_node<>* aXNode) +void TmxMap::ReadLayer(rapidxml::xml_node<>* aXNode) { - rapidxml::xml_attribute<>* xAttrib; - const char* name = ""; - int width = 0; - int height = 0; - uint32_t* tileDat = nullptr; + std::string_view name; + int width = 0, height = 0; // Read name - xAttrib = aXNode->first_attribute("name"); + auto xAttrib = aXNode->first_attribute("name"); if (xAttrib != nullptr) name = xAttrib->value(); @@ -86,34 +115,29 @@ void TmxReader::ReadLayer(rapidxml::xml_node<>* aXNode) // Read tile data auto xData = aXNode->first_node("data"); - if (xData != nullptr) - { - // TODO: don't assume base64 & zlib - tileDat = new uint32_t[width * height]; - if (!DecodeMap(tileDat, width * height * sizeof(uint32_t), std::string(xData->value()))) - { - delete[] tileDat; - tileDat = nullptr; - } - } + if (xData == nullptr) + return; - mLayers.push_back(new TmxLayer(width, height, name, tileDat)); + // TODO: don't assume base64 + std::vector tileDat(width * height); + if (!Decode(tileDat, xData->value())) + return; + + mLayers.emplace_back(TmxLayer(width, height, name, std::move(tileDat))); } -void TmxReader::ReadObjects(rapidxml::xml_node<>* aXNode) +void TmxMap::ReadObjects(rapidxml::xml_node<>* aXNode) { for (auto xNode = aXNode->first_node(); xNode != nullptr; xNode = xNode->next_sibling()) { if (strcmp(xNode->name(), "object") != 0) continue; - rapidxml::xml_attribute<>* xAttrib; - const char* name = ""; - float x = 0.0f; - float y = 0.0f; + std::string_view name; + float x = 0.0f, y = 0.0f; // Read name - xAttrib = xNode->first_attribute("name"); + auto xAttrib = xNode->first_attribute("name"); if (xAttrib != nullptr) name = xAttrib->value(); @@ -127,27 +151,16 @@ void TmxReader::ReadObjects(rapidxml::xml_node<>* aXNode) if (xAttrib != nullptr) y = std::stof(xAttrib->value()); - mObjects.push_back(new TmxObject(name, x, y)); + mObjects.emplace_back(TmxObject(name, x, y)); } } -void TmxReader::Open(std::istream& aIn) +bool TmxMap::Load(const std::string_view inPath) { - // Delete old tilesets - for (auto tileset : mTilesets) - delete tileset; - mTilesets.clear(); - - // Delete old layers - for (auto layer : mLayers) - delete layer; - mLayers.clear(); - - mGidTable.clear(); - - // Read string into a buffer + // Read file into a buffer + auto inFile = std::ifstream(inPath); std::stringstream buf; - buf << aIn.rdbuf(); + buf << inFile.rdbuf(); std::string strXml = buf.str(); buf.clear(); @@ -158,7 +171,7 @@ void TmxReader::Open(std::istream& aIn) // Get map node auto xMap = xDoc.first_node("map"); if (xMap == nullptr) - return; + return false; // Read map attribs rapidxml::xml_attribute<>* xAttrib = nullptr; @@ -171,20 +184,16 @@ void TmxReader::Open(std::istream& aIn) for (auto xNode = xMap->first_node(); xNode != nullptr; xNode = xNode->next_sibling()) { // Read layer nodes - if (strcmp(xNode->name(), "layer") == 0) + const auto xName = xNode->name(); + if (std::strcmp(xName, "layer") == 0) ReadLayer(xNode); - else - if (strcmp(xNode->name(), "tileset") == 0) + else if (std::strcmp(xName, "tileset") == 0) ReadTileset(xNode); - else - if (strcmp(xNode->name(), "objectgroup") == 0) + else if (std::strcmp(xName, "objectgroup") == 0) ReadObjects(xNode); } - // Generate global id table - for (auto tileset : mTilesets) - mGidTable.push_back(tileset->GetFirstGid()); - std::sort(mGidTable.rbegin(), mGidTable.rend()); + return true; } TmxReader::Error TmxReader::Open(const std::string& inPath, @@ -193,98 +202,102 @@ TmxReader::Error TmxReader::Open(const std::string& inPath, const std::string_view collisionName, const std::map& objMapping) { - tmx::Map map; - if (!map.load(inPath)) + TmxMap map; + if (!map.Load(inPath)) return Error::LOAD_FAILED; - using tmx::TileLayer; - using tmx::ObjectGroup; using std::optional; using std::reference_wrapper; - optional> layerGfx; - optional> layerCls; - optional> layerPal; - std::vector> objGroups; + optional> layerGfx; + optional> layerCls; + optional> layerPal; + optional>> objGroups; // Read layers - for (const auto& layer : map.getLayers()) + for (const auto& layer : map.Layers()) { - auto name = layer->getName(); - if (layer->getType() == tmx::Layer::Type::Tile) - { - const auto& tileLayer = layer->getLayerAs(); - // tmxlite unfortunately has no error reporting when a layer fails to load, - // empty check will suffice for the time being - if (tileLayer.getTiles().empty()) - continue; + auto name = layer.Name(); + //FIXME: no error reporting when a layer fails to load + if (layer.Tiles().empty()) + continue; - if (layerGfx == std::nullopt && (graphicsName.empty() || name == graphicsName)) - layerGfx = tileLayer; - if (!collisionName.empty() && layerCls == std::nullopt && name == collisionName) - layerCls = tileLayer; - if (!paletteName.empty() && layerPal == std::nullopt && name == paletteName) - layerPal = tileLayer; - } + if (!layerGfx.has_value() && (graphicsName.empty() || name == graphicsName)) { layerGfx = layer; } + if (!collisionName.empty() && !layerCls.has_value() && name == collisionName) { layerCls = layer; } + if (!paletteName.empty() && !layerPal.has_value() && name == paletteName) { layerPal = layer; } + /* else if (!objMapping.empty() && layer->getType() == tmx::Layer::Type::Object) { objGroups.emplace_back(layer->getLayerAs()); } + */ } // Check layers - if (layerGfx == std::nullopt) - { - if (graphicsName.empty()) - return Error::NO_LAYERS; - else - return Error::GRAPHICS_NOTFOUND; - } - if (layerCls == std::nullopt && !collisionName.empty()) + if (!layerGfx.has_value()) + return graphicsName.empty() + ? Error::NO_LAYERS + : Error::GRAPHICS_NOTFOUND; + if (!layerCls.has_value() && !collisionName.empty()) return Error::GRAPHICS_NOTFOUND; - if (layerPal == std::nullopt && !paletteName.empty()) + if (!layerPal.has_value() && !paletteName.empty()) return Error::PALETTE_NOTFOUND; // Read TMX map - mSize = Size{ map.getTileCount().x, map.getTileCount().y }; + mSize = Size{ map.TileCount().first, map.TileCount().second }; size_t numTiles = static_cast(mSize.width) * static_cast(mSize.height); // Read graphics layer mGraphics.reserve(numTiles); - for (auto tmxTile : layerGfx.value().get().getTiles()) - mGraphics.emplace_back(Tile { tmxTile.ID, tmxTile.flipFlags }); + for (auto tmxTile : layerGfx.value().get().Tiles()) + mGraphics.emplace_back(Tile{ tmxTile & ~FLIP_MASK, static_cast((tmxTile & FLIP_MASK) >> 28) }); // Read optional layers if (layerPal.has_value()) { std::vector v; v.reserve(numTiles); - for (auto tmxTile : layerPal.value().get().getTiles()) - v.emplace_back(tmxTile.ID); + for (auto tmxTile : layerPal.value().get().Tiles()) + v.emplace_back(tmxTile & ~FLIP_MASK); mPalette.emplace(v); } if (layerCls.has_value()) { std::vector v; v.reserve(numTiles); - for (auto tmxTile : layerCls.value().get().getTiles()) - v.emplace_back(tmxTile.ID); + for (auto tmxTile : layerCls.value().get().Tiles()) + v.emplace_back(tmxTile & ~FLIP_MASK); mCollision.emplace(v); } // Read tilesets - const auto& tilesets = map.getTilesets(); + const auto& tilesets = map.Tilesets(); mGidTable.reserve(tilesets.size()); for (const auto& set : tilesets) - mGidTable.emplace_back(std::make_pair(set.getFirstGID(), set.getLastGID())); + mGidTable.emplace_back(set.GidRange()); // Read objects if (!objMapping.empty()) { std::vector v; + for (const auto& tmxObj : objGroups.value().get()) + { + auto it = objMapping.find(std::string(tmxObj.Name())); + if (it == objMapping.end()) + continue; + + const auto& pos = tmxObj.Pos(); + Object obj; + obj.id = it->second; + obj.x = pos.x; + obj.y = pos.y; + + v.emplace_back(obj); + } + /* for (const auto& group : objGroups) { - const auto& tmxObjects = group.get().getObjects(); + const auto& tmxObjects = group.get().Objects(); v.reserve(v.size() + tmxObjects.size()); for (const auto& tmxObj : tmxObjects) { @@ -301,6 +314,7 @@ TmxReader::Error TmxReader::Open(const std::string& inPath, v.emplace_back(obj); } } + */ mObjects.emplace(v); } diff --git a/src/tmxreader.hpp b/src/tmxreader.hpp index afa6ea5..f98fb13 100644 --- a/src/tmxreader.hpp +++ b/src/tmxreader.hpp @@ -1,4 +1,4 @@ -/* tmxreader.hpp - Copyright (C) 2015-2022 a dinosaur (zlib, see COPYING.txt) */ +/* tmxreader.hpp - Copyright (C) 2015-2024 a dinosaur (zlib, see COPYING.txt) */ #ifndef TMXREADER_HPP #define TMXREADER_HPP @@ -10,11 +10,6 @@ #include #include #include -#include - -class TmxTileset; -class TmxLayer; -class TmxObject; class TmxReader { diff --git a/src/tmxtileset.hpp b/src/tmxtileset.hpp index bfa9817..c0dc201 100644 --- a/src/tmxtileset.hpp +++ b/src/tmxtileset.hpp @@ -1,28 +1,24 @@ -/* tmxtileset.hpp - Copyright (C) 2015-2022 a dinosaur (zlib, see COPYING.txt) */ +/* tmxtileset.hpp - Copyright (C) 2015-2024 a dinosaur (zlib, see COPYING.txt) */ #ifndef TMXTILESET_HPP #define TMXTILESET_HPP #include +#include #include -#include class TmxTileset { + std::string mName, mSource; + uint32_t mFirstGid = 0, mLastGid = 0; + public: - TmxTileset() : mFirstGid(0) {} - TmxTileset(std::string aName, std::string aSource, uint32_t aFirstGid) - : mName(std::move(aName)), mSource(std::move(aSource)), mFirstGid(aFirstGid) {} - ~TmxTileset() = default; + TmxTileset(const std::string_view name, const std::string_view source, uint32_t firstGid, uint32_t lastGid) + : mName(name), mSource(source), mFirstGid(firstGid), mLastGid(lastGid) {} - constexpr const std::string& GetName() const { return mName; } - constexpr const std::string& GetSource() const { return mSource; } - constexpr uint32_t GetFirstGid() const { return mFirstGid; } - -private: - std::string mName; - std::string mSource; - uint32_t mFirstGid; + [[nodiscard]] constexpr const std::string_view Name() const noexcept { return mName; } + [[nodiscard]] constexpr const std::string_view Source() const noexcept { return mSource; } + [[nodiscard]] constexpr const std::pair GidRange() const noexcept { return { mFirstGid, mLastGid }; } }; #endif//TMXTILESET_HPP