mirror of
https://github.com/ScrelliCopter/tmx2gba.git
synced 2025-02-21 03:29:25 +11:00
break map reader into its own class
This commit is contained in:
@@ -4,6 +4,7 @@ add_executable(tmx2gba
|
|||||||
tmxlayer.hpp
|
tmxlayer.hpp
|
||||||
tmxobject.hpp
|
tmxobject.hpp
|
||||||
tmxtileset.hpp
|
tmxtileset.hpp
|
||||||
|
tmxmap.hpp tmxmap.cpp
|
||||||
tmxreader.hpp tmxreader.cpp
|
tmxreader.hpp tmxreader.cpp
|
||||||
convert.hpp convert.cpp
|
convert.hpp convert.cpp
|
||||||
headerwriter.hpp headerwriter.cpp
|
headerwriter.hpp headerwriter.cpp
|
||||||
|
|||||||
@@ -12,8 +12,7 @@ bool convert::ConvertCharmap(std::vector<uint16_t>& out, int idxOffset, uint32_t
|
|||||||
|
|
||||||
const size_t numTiles = tmx.TileCount();
|
const size_t numTiles = tmx.TileCount();
|
||||||
assert(gfxTiles.size() == numTiles);
|
assert(gfxTiles.size() == numTiles);
|
||||||
if (palTiles.has_value())
|
assert(!palTiles.has_value() || palTiles.value().size() == numTiles);
|
||||||
assert(palTiles.value().size() == numTiles);
|
|
||||||
|
|
||||||
out.reserve(numTiles);
|
out.reserve(numTiles);
|
||||||
for (size_t i = 0; i < numTiles; ++i)
|
for (size_t i = 0; i < numTiles; ++i)
|
||||||
|
|||||||
169
src/tmxmap.cpp
Normal file
169
src/tmxmap.cpp
Normal file
@@ -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 <zlib.h>
|
||||||
|
#else
|
||||||
|
# include "gzip.hpp"
|
||||||
|
#endif
|
||||||
|
#include <sstream>
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
|
|
||||||
|
bool TmxMap::Decode(std::span<uint32_t> 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<uLongf>(sizeof(uint32_t) * out.size());
|
||||||
|
int res = uncompress(
|
||||||
|
reinterpret_cast<unsigned char*>(out.data()),
|
||||||
|
&dstSize,
|
||||||
|
reinterpret_cast<const unsigned char*>(decoded.data()),
|
||||||
|
static_cast<uLong>(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<uint32_t>(std::stoul(xAttrib->value()));
|
||||||
|
|
||||||
|
// Read last global ID
|
||||||
|
xAttrib = aXNode->first_attribute("lastgid");
|
||||||
|
if (xAttrib)
|
||||||
|
lastGid = static_cast<uint32_t>(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<uint32_t> 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<char*>(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;
|
||||||
|
}
|
||||||
36
src/tmxmap.hpp
Normal file
36
src/tmxmap.hpp
Normal file
@@ -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 <rapidxml/rapidxml.hpp>
|
||||||
|
#include <vector>
|
||||||
|
#include <span>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
class TmxMap
|
||||||
|
{
|
||||||
|
int mWidth = 0, mHeight = 0;
|
||||||
|
|
||||||
|
std::vector<TmxLayer> mLayers;
|
||||||
|
std::vector<TmxTileset> mTilesets;
|
||||||
|
std::vector<TmxObject> mObjects;
|
||||||
|
|
||||||
|
[[nodiscard]] bool Decode(std::span<uint32_t> 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<int, int> TileCount() const noexcept { return { mWidth, mHeight }; }
|
||||||
|
constexpr const std::vector<TmxTileset>& Tilesets() const noexcept { return mTilesets; }
|
||||||
|
constexpr const std::vector<TmxLayer>& Layers() const noexcept { return mLayers; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif//TMXMAP_HPP
|
||||||
@@ -1,201 +1,12 @@
|
|||||||
/* tmxreader.cpp - Copyright (C) 2015-2024 a dinosaur (zlib, see COPYING.txt) */
|
/* tmxreader.cpp - Copyright (C) 2015-2024 a dinosaur (zlib, see COPYING.txt) */
|
||||||
|
|
||||||
#include "tmxreader.hpp"
|
#include "tmxreader.hpp"
|
||||||
#include "tmxtileset.hpp"
|
#include "tmxmap.hpp"
|
||||||
#include "tmxobject.hpp"
|
|
||||||
#include "tmxlayer.hpp"
|
|
||||||
#include "base64.h"
|
|
||||||
#ifdef USE_ZLIB
|
|
||||||
# include <zlib.h>
|
|
||||||
#else
|
|
||||||
# include "gzip.hpp"
|
|
||||||
#endif
|
|
||||||
#include <rapidxml/rapidxml.hpp>
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
#include <sstream>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
|
|
||||||
class TmxMap
|
|
||||||
{
|
|
||||||
int mWidth = 0, mHeight = 0;
|
|
||||||
|
|
||||||
std::vector<TmxLayer> mLayers;
|
|
||||||
std::vector<TmxTileset> mTilesets;
|
|
||||||
std::vector<TmxObject> mObjects;
|
|
||||||
|
|
||||||
[[nodiscard]] bool Decode(std::span<uint32_t> 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<int, int> TileCount() const noexcept { return { mWidth, mHeight }; }
|
|
||||||
constexpr const std::vector<TmxTileset>& Tilesets() const noexcept { return mTilesets; }
|
|
||||||
constexpr const std::vector<TmxLayer>& Layers() const noexcept { return mLayers; }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
bool TmxMap::Decode(std::span<uint32_t> 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<uLongf>(sizeof(uint32_t) * out.size());
|
|
||||||
int res = uncompress(
|
|
||||||
reinterpret_cast<unsigned char*>(out.data()),
|
|
||||||
&dstSize,
|
|
||||||
reinterpret_cast<const unsigned char*>(decoded.data()),
|
|
||||||
static_cast<uLong>(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<uint32_t>(std::stoul(xAttrib->value()));
|
|
||||||
|
|
||||||
// Read last global ID
|
|
||||||
xAttrib = aXNode->first_attribute("lastgid");
|
|
||||||
if (xAttrib)
|
|
||||||
lastGid = static_cast<uint32_t>(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<uint32_t> 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<char*>(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,
|
TmxReader::Error TmxReader::Open(const std::string& inPath,
|
||||||
const std::string_view graphicsName,
|
const std::string_view graphicsName,
|
||||||
const std::string_view paletteName,
|
const std::string_view paletteName,
|
||||||
|
|||||||
Reference in New Issue
Block a user