From c2e9f5c9743e640bd7d42f8b1407587e340d6e8f Mon Sep 17 00:00:00 2001 From: a dinosaur Date: Wed, 10 Apr 2024 02:49:52 +1000 Subject: [PATCH] break map reader into its own class --- src/CMakeLists.txt | 1 + src/convert.cpp | 3 +- src/tmxmap.cpp | 169 +++++++++++++++++++++++++++++++++++++++ src/tmxmap.hpp | 36 +++++++++ src/tmxreader.cpp | 191 +-------------------------------------------- 5 files changed, 208 insertions(+), 192 deletions(-) create mode 100644 src/tmxmap.cpp create mode 100644 src/tmxmap.hpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 7372822..7afbc0e 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -4,6 +4,7 @@ add_executable(tmx2gba tmxlayer.hpp tmxobject.hpp tmxtileset.hpp + tmxmap.hpp tmxmap.cpp tmxreader.hpp tmxreader.cpp convert.hpp convert.cpp headerwriter.hpp headerwriter.cpp diff --git a/src/convert.cpp b/src/convert.cpp index 8cf26c8..de15bd3 100644 --- a/src/convert.cpp +++ b/src/convert.cpp @@ -12,8 +12,7 @@ bool convert::ConvertCharmap(std::vector& out, int idxOffset, uint32_t const size_t numTiles = tmx.TileCount(); assert(gfxTiles.size() == numTiles); - if (palTiles.has_value()) - assert(palTiles.value().size() == numTiles); + assert(!palTiles.has_value() || palTiles.value().size() == numTiles); out.reserve(numTiles); for (size_t i = 0; i < numTiles; ++i) diff --git a/src/tmxmap.cpp b/src/tmxmap.cpp new file mode 100644 index 0000000..040620f --- /dev/null +++ b/src/tmxmap.cpp @@ -0,0 +1,169 @@ +// SPDX-License-Identifier: Zlib +// SPDX-FileCopyrightText: (c) 2015-2024 a dinosaur + +#include "tmxmap.hpp" +#include "base64.h" +#ifdef USE_ZLIB +# include +#else +# include "gzip.hpp" +#endif +#include +#include + + +bool TmxMap::Decode(std::span out, const std::string_view base64) +{ + // Cut leading & trailing whitespace (including newlines) + auto beg = std::find_if_not(base64.begin(), base64.end(), ::isspace); + if (beg == std::end(base64)) + return false; + 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(sizeof(uint32_t) * out.size()); + int res = uncompress( + reinterpret_cast(out.data()), + &dstSize, + reinterpret_cast(decoded.data()), + static_cast(decoded.size())); + + return res >= 0; +} + +void TmxMap::ReadTileset(rapidxml::xml_node<>* aXNode) +{ + std::string_view name, source; + uint32_t firstGid = 0, lastGid = 0; + + // Read name + auto xAttrib = aXNode->first_attribute("name"); + if (xAttrib != nullptr) + name = xAttrib->value(); + + // Read source + xAttrib = aXNode->first_attribute("source"); + if (xAttrib != nullptr) + source = xAttrib->value(); + + // Read first global ID + xAttrib = aXNode->first_attribute("firstgid"); + if (xAttrib != nullptr) + firstGid = static_cast(std::stoul(xAttrib->value())); + + // 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 TmxMap::ReadLayer(rapidxml::xml_node<>* aXNode) +{ + std::string_view name; + int width = 0, height = 0; + + // Read name + auto xAttrib = aXNode->first_attribute("name"); + if (xAttrib != nullptr) + name = xAttrib->value(); + + // Read width + xAttrib = aXNode->first_attribute("width"); + if (xAttrib != nullptr) + width = std::stoi(xAttrib->value()); + + // Read height + xAttrib = aXNode->first_attribute("height"); + if (xAttrib != nullptr) + height = std::stoi(xAttrib->value()); + + // Read tile data + auto xData = aXNode->first_node("data"); + if (xData == nullptr) + return; + + // 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 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; + + std::string_view name; + float x = 0.0f, y = 0.0f; + + // Read name + auto xAttrib = xNode->first_attribute("name"); + if (xAttrib != nullptr) + name = xAttrib->value(); + + // Read X pos + xAttrib = xNode->first_attribute("x"); + if (xAttrib != nullptr) + x = std::stof(xAttrib->value()); + + // Read Y pos + xAttrib = xNode->first_attribute("y"); + if (xAttrib != nullptr) + y = std::stof(xAttrib->value()); + + mObjects.emplace_back(TmxObject(name, x, y)); + } +} + +bool TmxMap::Load(const std::string_view inPath) +{ + // Read file into a buffer + auto inFile = std::ifstream(inPath); + std::stringstream buf; + buf << inFile.rdbuf(); + std::string strXml = buf.str(); + buf.clear(); + + // Parse document + rapidxml::xml_document<> xDoc; + xDoc.parse<0>(const_cast(strXml.c_str())); + + // Get map node + auto xMap = xDoc.first_node("map"); + if (xMap == nullptr) + return false; + + // Read map attribs + rapidxml::xml_attribute<>* xAttrib = nullptr; + if ((xAttrib = xMap->first_attribute("width")) != nullptr) + mWidth = std::stoi(xAttrib->value()); + if ((xAttrib = xMap->first_attribute("height")) != nullptr) + mHeight = std::stoi(xAttrib->value()); + + // Read nodes + for (auto xNode = xMap->first_node(); xNode != nullptr; xNode = xNode->next_sibling()) + { + // Read layer nodes + const auto xName = xNode->name(); + if (std::strcmp(xName, "layer") == 0) + ReadLayer(xNode); + else if (std::strcmp(xName, "tileset") == 0) + ReadTileset(xNode); + else if (std::strcmp(xName, "objectgroup") == 0) + ReadObjects(xNode); + } + + return true; +} diff --git a/src/tmxmap.hpp b/src/tmxmap.hpp new file mode 100644 index 0000000..65f2fe3 --- /dev/null +++ b/src/tmxmap.hpp @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: Zlib +// SPDX-FileCopyrightText: (c) 2015-2024 a dinosaur + +#ifndef TMXMAP_HPP +#define TMXMAP_HPP + +#include "tmxtileset.hpp" +#include "tmxobject.hpp" +#include "tmxlayer.hpp" +#include +#include +#include +#include + +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; } +}; + +#endif//TMXMAP_HPP diff --git a/src/tmxreader.cpp b/src/tmxreader.cpp index 98c1683..06109e1 100644 --- a/src/tmxreader.cpp +++ b/src/tmxreader.cpp @@ -1,201 +1,12 @@ /* tmxreader.cpp - Copyright (C) 2015-2024 a dinosaur (zlib, see COPYING.txt) */ #include "tmxreader.hpp" -#include "tmxtileset.hpp" -#include "tmxobject.hpp" -#include "tmxlayer.hpp" -#include "base64.h" -#ifdef USE_ZLIB -# include -#else -# include "gzip.hpp" -#endif -#include +#include "tmxmap.hpp" #include #include #include -#include -#include -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(base64.begin(), base64.end(), ::isspace); - if (beg == std::end(base64)) - return false; - 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(sizeof(uint32_t) * out.size()); - int res = uncompress( - reinterpret_cast(out.data()), - &dstSize, - reinterpret_cast(decoded.data()), - static_cast(decoded.size())); - - return res >= 0; -} - -void TmxMap::ReadTileset(rapidxml::xml_node<>* aXNode) -{ - std::string_view name, source; - uint32_t firstGid = 0, lastGid = 0; - - // Read name - auto xAttrib = aXNode->first_attribute("name"); - if (xAttrib != nullptr) - name = xAttrib->value(); - - // Read source - xAttrib = aXNode->first_attribute("source"); - if (xAttrib != nullptr) - source = xAttrib->value(); - - // Read first global ID - xAttrib = aXNode->first_attribute("firstgid"); - if (xAttrib != nullptr) - firstGid = static_cast(std::stoul(xAttrib->value())); - - // 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 TmxMap::ReadLayer(rapidxml::xml_node<>* aXNode) -{ - std::string_view name; - int width = 0, height = 0; - - // Read name - auto xAttrib = aXNode->first_attribute("name"); - if (xAttrib != nullptr) - name = xAttrib->value(); - - // Read width - xAttrib = aXNode->first_attribute("width"); - if (xAttrib != nullptr) - width = std::stoi(xAttrib->value()); - - // Read height - xAttrib = aXNode->first_attribute("height"); - if (xAttrib != nullptr) - height = std::stoi(xAttrib->value()); - - // Read tile data - auto xData = aXNode->first_node("data"); - if (xData == nullptr) - return; - - // 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 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; - - std::string_view name; - float x = 0.0f, y = 0.0f; - - // Read name - auto xAttrib = xNode->first_attribute("name"); - if (xAttrib != nullptr) - name = xAttrib->value(); - - // Read X pos - xAttrib = xNode->first_attribute("x"); - if (xAttrib != nullptr) - x = std::stof(xAttrib->value()); - - // Read Y pos - xAttrib = xNode->first_attribute("y"); - if (xAttrib != nullptr) - y = std::stof(xAttrib->value()); - - mObjects.emplace_back(TmxObject(name, x, y)); - } -} - -bool TmxMap::Load(const std::string_view inPath) -{ - // Read file into a buffer - auto inFile = std::ifstream(inPath); - std::stringstream buf; - buf << inFile.rdbuf(); - std::string strXml = buf.str(); - buf.clear(); - - // Parse document - rapidxml::xml_document<> xDoc; - xDoc.parse<0>(const_cast(strXml.c_str())); - - // Get map node - auto xMap = xDoc.first_node("map"); - if (xMap == nullptr) - return false; - - // Read map attribs - rapidxml::xml_attribute<>* xAttrib = nullptr; - if ((xAttrib = xMap->first_attribute("width")) != nullptr) - mWidth = std::stoi(xAttrib->value()); - if ((xAttrib = xMap->first_attribute("height")) != nullptr) - mHeight = std::stoi(xAttrib->value()); - - // Read nodes - for (auto xNode = xMap->first_node(); xNode != nullptr; xNode = xNode->next_sibling()) - { - // Read layer nodes - const auto xName = xNode->name(); - if (std::strcmp(xName, "layer") == 0) - ReadLayer(xNode); - else if (std::strcmp(xName, "tileset") == 0) - ReadTileset(xNode); - else if (std::strcmp(xName, "objectgroup") == 0) - ReadObjects(xNode); - } - - return true; -} - TmxReader::Error TmxReader::Open(const std::string& inPath, const std::string_view graphicsName, const std::string_view paletteName,