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:
@@ -1,28 +1,20 @@
|
||||
add_executable(tmx2gba
|
||||
argparse.hpp argparse.cpp
|
||||
tmxlayer.hpp
|
||||
tmxobject.hpp
|
||||
tmxreader.hpp tmxreader.cpp
|
||||
tmxtileset.hpp
|
||||
swriter.hpp swriter.cpp
|
||||
convert.hpp convert.cpp
|
||||
headerwriter.hpp headerwriter.cpp
|
||||
swriter.hpp swriter.cpp
|
||||
tmx2gba.cpp)
|
||||
|
||||
set_target_properties(tmx2gba PROPERTIES
|
||||
# C++20 & C99
|
||||
CXX_STANDARD 20
|
||||
C_STANDARD 99)
|
||||
set_target_properties(tmx2gba PROPERTIES CXX_STANDARD 20)
|
||||
|
||||
# Enable strong warnings
|
||||
target_compile_options(tmx2gba PRIVATE
|
||||
$<$<CXX_COMPILER_ID:MSVC>:/Wall>
|
||||
$<$<CXX_COMPILER_ID:GNU,Clang,AppleClang>:-Wall -Wextra -pedantic>
|
||||
$<$<CXX_COMPILER_ID:Clang,AppleClang>:-Weverything -Wno-c++98-compat>)
|
||||
$<$<CXX_COMPILER_ID:Clang,AppleClang>:-Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded>)
|
||||
|
||||
target_link_libraries(tmx2gba
|
||||
External::base64
|
||||
External::miniz
|
||||
External::rapidxml)
|
||||
target_link_libraries(tmx2gba tmxlite)
|
||||
|
||||
if (TMX2GBA_DKP_INSTALL)
|
||||
if (DEFINED ENV{DEVKITPRO})
|
||||
|
||||
67
src/convert.cpp
Normal file
67
src/convert.cpp
Normal file
@@ -0,0 +1,67 @@
|
||||
/* converter.hpp - Copyright (C) 2015-2024 a dinosaur (zlib, see COPYING.txt) */
|
||||
|
||||
#include "convert.hpp"
|
||||
#include "tmxreader.hpp"
|
||||
|
||||
|
||||
bool convert::ConvertCharmap(std::vector<uint16_t>& out, int idxOffset, uint32_t defaultPal, const TmxReader& tmx)
|
||||
{
|
||||
const auto gfxTiles = tmx.GetGraphicsTiles();
|
||||
const auto palTiles = tmx.GetPaletteTiles();
|
||||
|
||||
const size_t numTiles = tmx.TileCount();
|
||||
out.reserve(numTiles);
|
||||
for (size_t i = 0; i < numTiles; ++i)
|
||||
{
|
||||
const TmxReader::Tile tile = gfxTiles[i];
|
||||
|
||||
int tileIdx = std::max(0, static_cast<int>(tmx.LidFromGid(tile.id)) + idxOffset);
|
||||
uint8_t flags = 0x0;
|
||||
|
||||
// Get flipped!
|
||||
flags |= (tile.flags & TmxReader::FLIP_HORZ) ? 0x4 : 0x0;
|
||||
flags |= (tile.flags & TmxReader::FLIP_VERT) ? 0x8 : 0x0;
|
||||
|
||||
// Determine palette ID
|
||||
uint32_t idx = 0;
|
||||
if (palTiles.has_value())
|
||||
idx = tmx.LidFromGid(palTiles.value()[i]);
|
||||
if (idx == 0)
|
||||
idx = defaultPal + 1;
|
||||
flags |= static_cast<uint8_t>(idx - 1) << 4;
|
||||
|
||||
out.push_back(static_cast<uint16_t>(tileIdx) | static_cast<uint16_t>(flags << 8));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool convert::ConvertCollision(std::vector<uint8_t>& out, const TmxReader& tmx)
|
||||
{
|
||||
const auto clsTiles = tmx.GetCollisionTiles().value();
|
||||
|
||||
size_t numTiles = tmx.TileCount();
|
||||
out.reserve(numTiles);
|
||||
for (size_t i = 0; i < numTiles; ++i)
|
||||
{
|
||||
uint8_t id = static_cast<uint8_t>(tmx.LidFromGid(clsTiles[i]));
|
||||
out.emplace_back(id);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool convert::ConvertObjects(std::vector<uint32_t>& out, const TmxReader& tmx)
|
||||
{
|
||||
const auto objects = tmx.GetObjects().value();
|
||||
|
||||
for (auto obj : objects)
|
||||
{
|
||||
out.push_back(obj.id);
|
||||
out.emplace_back(static_cast<int>(obj.x * 256.0f));
|
||||
out.emplace_back(static_cast<int>(obj.y * 256.0f));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
20
src/convert.hpp
Normal file
20
src/convert.hpp
Normal file
@@ -0,0 +1,20 @@
|
||||
/* converter.hpp - Copyright (C) 2024 a dinosaur (zlib, see COPYING.txt) */
|
||||
|
||||
#ifndef CONVERT_HPP
|
||||
#define CONVERT_HPP
|
||||
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
|
||||
class TmxReader;
|
||||
|
||||
namespace convert
|
||||
{
|
||||
[[nodiscard]] bool ConvertCharmap(std::vector<uint16_t>& out,
|
||||
int idOffset, uint32_t defaultPalIdx,
|
||||
const TmxReader& tmx);
|
||||
[[nodiscard]] bool ConvertCollision(std::vector<uint8_t>& out, const TmxReader& tmx);
|
||||
[[nodiscard]] bool ConvertObjects(std::vector<uint32_t>& out, const TmxReader& tmx);
|
||||
};
|
||||
|
||||
#endif//CONVERT_HPP
|
||||
@@ -8,7 +8,7 @@ template <> constexpr std::string_view DatType<uint8_t>() { return "unsigned cha
|
||||
template <> constexpr std::string_view DatType<uint16_t>() { return "unsigned short"; }
|
||||
template <> constexpr std::string_view DatType<uint32_t>() { return "unsigned int"; }
|
||||
|
||||
void HeaderWriter::WriteSize(int width, int height)
|
||||
void HeaderWriter::WriteSize(unsigned width, unsigned height)
|
||||
{
|
||||
stream << std::endl;
|
||||
WriteDefine(mName + "Width", width);
|
||||
|
||||
@@ -37,7 +37,7 @@ public:
|
||||
WriteDefine(name, std::to_string(value));
|
||||
}
|
||||
|
||||
void WriteSize(int width, int height);
|
||||
void WriteSize(unsigned width, unsigned height);
|
||||
void WriteCharacterMap(const std::span<uint16_t> charData);
|
||||
void WriteCollision(const std::span<uint8_t> collisionData);
|
||||
void WriteObjects(const std::span<uint32_t> objData);
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "swriter.hpp"
|
||||
#include <type_traits>
|
||||
#include <limits>
|
||||
#include <assert.h>
|
||||
|
||||
#define GNU_STYLE 0
|
||||
#define MASM_STYLE 1
|
||||
@@ -130,31 +131,26 @@ void SWriter::WriteSymbol(const std::string_view suffix)
|
||||
|
||||
void SWriter::WriteArray(const std::string_view suffix, std::span<uint8_t> data, int numCols)
|
||||
{
|
||||
assert(data.size());
|
||||
WriteSymbol(suffix);
|
||||
WriteArrayDetail(stream, data.begin(), data.end(), numCols);
|
||||
}
|
||||
|
||||
void SWriter::WriteArray(const std::string_view suffix, std::span<uint16_t> data, int numCols)
|
||||
{
|
||||
assert(data.size());
|
||||
WriteSymbol(suffix);
|
||||
WriteArrayDetail(stream, data.begin(), data.end(), numCols);
|
||||
}
|
||||
|
||||
void SWriter::WriteArray(const std::string_view suffix, std::span<uint32_t> data, int numCols)
|
||||
{
|
||||
assert(data.size());
|
||||
WriteSymbol(suffix);
|
||||
WriteArrayDetail(stream, data.begin(), data.end(), numCols);
|
||||
}
|
||||
|
||||
|
||||
SWriter::~SWriter()
|
||||
{
|
||||
if (stream.is_open())
|
||||
{
|
||||
stream.close();
|
||||
}
|
||||
}
|
||||
|
||||
bool SWriter::Open(const std::filesystem::path& path, const std::string_view name)
|
||||
{
|
||||
mName = name;
|
||||
|
||||
@@ -20,9 +20,7 @@ class SWriter
|
||||
void WriteSymbol(const std::string_view suffix);
|
||||
|
||||
public:
|
||||
~SWriter();
|
||||
|
||||
bool Open(const std::filesystem::path& path, const std::string_view name);
|
||||
[[nodiscard]] bool Open(const std::filesystem::path& path, const std::string_view name);
|
||||
|
||||
void WriteArray(const std::string_view suffix, std::span<uint8_t> data, int numCols = 16);
|
||||
void WriteArray(const std::string_view suffix, std::span<uint16_t> data, int numCols = 16);
|
||||
|
||||
122
src/tmx2gba.cpp
122
src/tmx2gba.cpp
@@ -2,8 +2,7 @@
|
||||
|
||||
#include "argparse.hpp"
|
||||
#include "tmxreader.hpp"
|
||||
#include "tmxlayer.hpp"
|
||||
#include "tmxobject.hpp"
|
||||
#include "convert.hpp"
|
||||
#include "headerwriter.hpp"
|
||||
#include "swriter.hpp"
|
||||
#include <iostream>
|
||||
@@ -15,13 +14,13 @@ static const char* versionStr = "tmx2gba version 0.3, (c) 2015-2022 a dinosaur";
|
||||
|
||||
struct Arguments
|
||||
{
|
||||
bool help = false, showVersion = false;
|
||||
std::string inPath, outPath;
|
||||
std::string layer, collisionlay, paletteLay;
|
||||
std::string flagFile;
|
||||
int offset = 0;
|
||||
int palette = 0;
|
||||
std::vector<std::string> objMappings;
|
||||
bool help = false, showVersion = false;
|
||||
};
|
||||
|
||||
using ArgParse::Option;
|
||||
@@ -163,34 +162,26 @@ int main(int argc, char** argv)
|
||||
|
||||
// Open & read input file
|
||||
TmxReader tmx;
|
||||
std::ifstream fin(p.inPath);
|
||||
if (!fin.is_open())
|
||||
switch (tmx.Open(p.inPath,
|
||||
p.layer, p.paletteLay, p.collisionlay, objMapping))
|
||||
{
|
||||
case TmxReader::Error::LOAD_FAILED:
|
||||
std::cerr << "Failed to open input file." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
tmx.Open(fin);
|
||||
|
||||
// Get layers
|
||||
if (tmx.GetLayerCount() == 0)
|
||||
{
|
||||
std::cerr << "No layers found." << std::endl;
|
||||
case TmxReader::Error::NO_LAYERS:
|
||||
std::cerr << "No suitable tile layer found." << std::endl;
|
||||
return 1;
|
||||
}
|
||||
const TmxLayer* layerGfx = p.layer.empty()
|
||||
? tmx.GetLayer(0)
|
||||
: tmx.GetLayer(p.layer);
|
||||
const TmxLayer* layerCls = p.collisionlay.empty()
|
||||
? nullptr
|
||||
: tmx.GetLayer(p.collisionlay);
|
||||
const TmxLayer* layerPal = p.paletteLay.empty()
|
||||
? nullptr
|
||||
: tmx.GetLayer(p.paletteLay);
|
||||
|
||||
if (layerGfx == nullptr)
|
||||
{
|
||||
std::cerr << "Input layer not found." << std::endl;
|
||||
case TmxReader::Error::GRAPHICS_NOTFOUND:
|
||||
std::cerr << "No graphics layer \"" << p.layer << "\" found." << std::endl;
|
||||
return 1;
|
||||
case TmxReader::Error::PALETTE_NOTFOUND:
|
||||
std::cerr << "No palette layer \"" << p.paletteLay << "\" found." << std::endl;
|
||||
return 1;
|
||||
case TmxReader::Error::COLLISION_NOTFOUND:
|
||||
std::cerr << "No collision layer \"" << p.collisionlay << "\" found." << std::endl;
|
||||
return 1;
|
||||
case TmxReader::Error::OK:
|
||||
break;
|
||||
}
|
||||
|
||||
// Get name from file
|
||||
@@ -203,12 +194,13 @@ int main(int argc, char** argv)
|
||||
name = name.substr(slashPos + 1);
|
||||
|
||||
// Open output files
|
||||
SWriter outS; HeaderWriter outH;
|
||||
SWriter outS;
|
||||
if (!outS.Open(p.outPath + ".s", name))
|
||||
{
|
||||
std::cerr << "Failed to create output file \"" << p.outPath << ".s\".";
|
||||
return 1;
|
||||
}
|
||||
HeaderWriter outH;
|
||||
if (!outH.Open(p.outPath + ".h", name))
|
||||
{
|
||||
std::cerr << "Failed to create output file \"" << p.outPath << ".h\".";
|
||||
@@ -216,82 +208,34 @@ int main(int argc, char** argv)
|
||||
}
|
||||
|
||||
// Convert to GBA-friendly charmap data
|
||||
const uint32_t* gfxTiles = layerGfx->GetData();
|
||||
const uint32_t* palTiles = (layerPal == nullptr) ? nullptr : layerPal->GetData();
|
||||
std::vector<uint16_t> charDat;
|
||||
const size_t numTiles = static_cast<size_t>(layerGfx->GetWidth()) * static_cast<size_t>(layerGfx->GetHeight());
|
||||
charDat.reserve(numTiles);
|
||||
for (size_t i = 0; i < numTiles; ++i)
|
||||
{
|
||||
uint32_t read = (*gfxTiles++);
|
||||
std::vector<uint16_t> charDat;
|
||||
if (!convert::ConvertCharmap(charDat, p.offset, p.palette, tmx))
|
||||
return 1;
|
||||
|
||||
uint16_t tile = std::max(0, static_cast<int>(tmx.LidFromGid(read & ~TmxLayer::FLIP_MASK)) + p.offset);
|
||||
uint8_t flags = 0x0;
|
||||
|
||||
// Get flipped!
|
||||
flags |= (read & TmxLayer::FLIP_HORZ) ? 0x4 : 0x0;
|
||||
flags |= (read & TmxLayer::FLIP_VERT) ? 0x8 : 0x0;
|
||||
|
||||
// Determine palette ID
|
||||
uint32_t idx = 0;
|
||||
if (palTiles != nullptr)
|
||||
idx = tmx.LidFromGid((*palTiles++) & ~TmxLayer::FLIP_MASK);
|
||||
if (idx == 0)
|
||||
idx = p.palette + 1;
|
||||
flags |= static_cast<uint8_t>(idx - 1) << 4;
|
||||
|
||||
charDat.push_back(tile | (static_cast<uint16_t>(flags) << 8));
|
||||
// Write out charmap
|
||||
outH.WriteSize(tmx.GetSize().width, tmx.GetSize().height);
|
||||
outH.WriteCharacterMap(charDat);
|
||||
outS.WriteArray("Tiles", charDat);
|
||||
}
|
||||
|
||||
// Write out charmap
|
||||
outH.WriteSize(tmx.GetWidth(), tmx.GetHeight());
|
||||
outH.WriteCharacterMap(charDat);
|
||||
outS.WriteArray("Tiles", charDat);
|
||||
|
||||
// Convert collision map & write it out
|
||||
if (layerCls != nullptr)
|
||||
// Convert collision map & write out
|
||||
if (tmx.HasCollisionTiles())
|
||||
{
|
||||
std::vector<uint8_t> collisionDat;
|
||||
collisionDat.reserve(layerCls->GetWidth() * layerCls->GetHeight());
|
||||
if (!convert::ConvertCollision(collisionDat, tmx))
|
||||
return 1;
|
||||
|
||||
gfxTiles = layerCls->GetData();
|
||||
for (int i = 0; i < layerCls->GetWidth() * layerCls->GetHeight(); ++i)
|
||||
{
|
||||
uint8_t ucTile = static_cast<uint8_t>(tmx.LidFromGid((*gfxTiles++) & ~TmxLayer::FLIP_MASK));
|
||||
collisionDat.push_back(ucTile);
|
||||
}
|
||||
|
||||
// Try to nicely append "_collision" to the output name
|
||||
std::string path;
|
||||
size_t extPos = p.outPath.find_last_of('.');
|
||||
if (extPos != std::string::npos)
|
||||
path = p.outPath.insert(extPos, "_collision");
|
||||
else
|
||||
path = p.outPath + "_collision";
|
||||
|
||||
// Write collision
|
||||
outH.WriteCollision(collisionDat);
|
||||
outS.WriteArray("Collision", collisionDat, 32);
|
||||
}
|
||||
|
||||
if (!p.objMappings.empty())
|
||||
if (tmx.HasObjects())
|
||||
{
|
||||
std::vector<uint32_t> objDat;
|
||||
for (size_t i = 0; i < tmx.GetObjectCount(); ++i)
|
||||
{
|
||||
auto obj = tmx.GetObject(i);
|
||||
auto it = objMapping.find(obj->GetName());
|
||||
if (it == objMapping.end())
|
||||
continue;
|
||||
if (!convert::ConvertObjects(objDat, tmx))
|
||||
return 1;
|
||||
|
||||
float x, y;
|
||||
obj->GetPos(x, y);
|
||||
objDat.push_back(it->second);
|
||||
objDat.push_back(static_cast<int>(x * 256.0f));
|
||||
objDat.push_back(static_cast<int>(y * 256.0f));
|
||||
}
|
||||
|
||||
// Write objects
|
||||
outH.WriteObjects(objDat);
|
||||
outS.WriteArray("Objdat", objDat);
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
/* tmxlayer.hpp - Copyright (C) 2015-2022 a dinosaur (zlib, see COPYING.txt) */
|
||||
|
||||
#ifndef TMXLAYER_HPP
|
||||
#define TMXLAYER_HPP
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
class TmxLayer
|
||||
{
|
||||
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; }
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
#endif//TMXLAYER_HPP
|
||||
@@ -1,25 +0,0 @@
|
||||
/* tmxobject.hpp - Copyright (C) 2015-2022 a dinosaur (zlib, see COPYING.txt) */
|
||||
|
||||
#ifndef TMXOBJECT_HPP
|
||||
#define TMXOBJECT_HPP
|
||||
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
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;
|
||||
|
||||
constexpr const std::string& GetName() const { return mName; }
|
||||
inline void GetPos(float& aOutX, float& aOutY) const { aOutX = mX; aOutY = mY; }
|
||||
|
||||
private:
|
||||
std::string mName;
|
||||
float mX, mY;
|
||||
};
|
||||
|
||||
#endif//TMXOBJECT_HPP
|
||||
@@ -1,227 +1,133 @@
|
||||
/* tmxreader.cpp - Copyright (C) 2015-2022 a dinosaur (zlib, see COPYING.txt) */
|
||||
/* 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 <cstdint>
|
||||
#include <sstream>
|
||||
#include "tmxlite/Map.hpp"
|
||||
#include "tmxlite/TileLayer.hpp"
|
||||
#include "tmxlite/ObjectGroup.hpp"
|
||||
#include <optional>
|
||||
#include <algorithm>
|
||||
#include <rapidxml/rapidxml.hpp>
|
||||
#include <base64.h>
|
||||
#include <miniz.h>
|
||||
|
||||
|
||||
TmxReader::~TmxReader()
|
||||
TmxReader::Error TmxReader::Open(const std::string& inPath,
|
||||
const std::string_view graphicsName,
|
||||
const std::string_view paletteName,
|
||||
const std::string_view collisionName,
|
||||
const std::map<std::string, uint32_t>& objMapping)
|
||||
{
|
||||
// Delete old tilesets
|
||||
for (auto pTileset : mTilesets)
|
||||
delete pTileset;
|
||||
mTilesets.clear();
|
||||
tmx::Map map;
|
||||
if (!map.load(inPath))
|
||||
return Error::LOAD_FAILED;
|
||||
|
||||
// Delete old layers
|
||||
for (auto pLay : mLayers)
|
||||
delete pLay;
|
||||
mLayers.clear();
|
||||
}
|
||||
using tmx::TileLayer;
|
||||
using tmx::ObjectGroup;
|
||||
using std::optional;
|
||||
using std::reference_wrapper;
|
||||
|
||||
optional<reference_wrapper<const TileLayer>> layerGfx;
|
||||
optional<reference_wrapper<const TileLayer>> layerCls;
|
||||
optional<reference_wrapper<const TileLayer>> layerPal;
|
||||
std::vector<reference_wrapper<const ObjectGroup>> objGroups;
|
||||
|
||||
bool TmxReader::DecodeMap(uint32_t* aOut, size_t aOutSize, const std::string& aBase64Dat)
|
||||
{
|
||||
// Cut leading & trailing whitespace (including newlines)
|
||||
auto beg = std::find_if_not(aBase64Dat.begin(), aBase64Dat.end(), ::isspace);
|
||||
if (beg == std::end(aBase64Dat))
|
||||
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);
|
||||
|
||||
// Decode base64 string
|
||||
std::string decoded = base64_decode(trimmed);
|
||||
|
||||
// Decompress compressed data
|
||||
auto dstSize = static_cast<mz_ulong>(aOutSize);
|
||||
int res = uncompress(
|
||||
reinterpret_cast<unsigned char*>(aOut),
|
||||
&dstSize,
|
||||
reinterpret_cast<const unsigned char*>(decoded.data()),
|
||||
static_cast<mz_ulong>(decoded.size()));
|
||||
decoded.clear();
|
||||
if (res < 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void TmxReader::ReadTileset(rapidxml::xml_node<>* aXNode)
|
||||
{
|
||||
rapidxml::xml_attribute<>* xAttrib;
|
||||
|
||||
const char* name = "";
|
||||
const char* source = "";
|
||||
uint32_t firstGid = 0;
|
||||
|
||||
// Read name
|
||||
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()));
|
||||
|
||||
mTilesets.push_back(new TmxTileset(name, source, firstGid));
|
||||
}
|
||||
|
||||
void TmxReader::ReadLayer(rapidxml::xml_node<>* aXNode)
|
||||
{
|
||||
rapidxml::xml_attribute<>* xAttrib;
|
||||
const char* name = "";
|
||||
int width = 0;
|
||||
int height = 0;
|
||||
uint32_t* tileDat = nullptr;
|
||||
|
||||
// Read name
|
||||
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)
|
||||
// Read layers
|
||||
for (const auto& layer : map.getLayers())
|
||||
{
|
||||
// TODO: don't assume base64 & zlib
|
||||
tileDat = new uint32_t[width * height];
|
||||
if (!DecodeMap(tileDat, width * height * sizeof(uint32_t), std::string(xData->value())))
|
||||
auto name = layer->getName();
|
||||
if (layer->getType() == tmx::Layer::Type::Tile)
|
||||
{
|
||||
delete[] tileDat;
|
||||
tileDat = nullptr;
|
||||
if (layerGfx == std::nullopt && (graphicsName.empty() || name == graphicsName))
|
||||
layerGfx = layer->getLayerAs<TileLayer>();
|
||||
if (!collisionName.empty() && layerCls == std::nullopt && name == collisionName)
|
||||
layerCls = layer->getLayerAs<TileLayer>();
|
||||
if (!paletteName.empty() && layerPal == std::nullopt && name == paletteName)
|
||||
layerPal = layer->getLayerAs<TileLayer>();
|
||||
}
|
||||
else if (!objMapping.empty() && layer->getType() == tmx::Layer::Type::Object)
|
||||
{
|
||||
objGroups.emplace_back(layer->getLayerAs<ObjectGroup>());
|
||||
}
|
||||
}
|
||||
|
||||
mLayers.push_back(new TmxLayer(width, height, name, tileDat));
|
||||
}
|
||||
|
||||
void TmxReader::ReadObjects(rapidxml::xml_node<>* aXNode)
|
||||
{
|
||||
for (auto xNode = aXNode->first_node(); xNode != nullptr; xNode = xNode->next_sibling())
|
||||
// Check layers
|
||||
if (layerGfx == std::nullopt)
|
||||
{
|
||||
if (strcmp(xNode->name(), "object") != 0)
|
||||
continue;
|
||||
|
||||
rapidxml::xml_attribute<>* xAttrib;
|
||||
const char* name = "";
|
||||
float x = 0.0f;
|
||||
float y = 0.0f;
|
||||
|
||||
// Read name
|
||||
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.push_back(new TmxObject(name, x, y));
|
||||
}
|
||||
}
|
||||
|
||||
void TmxReader::Open(std::istream& aIn)
|
||||
{
|
||||
// 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
|
||||
std::stringstream buf;
|
||||
buf << aIn.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;
|
||||
|
||||
// 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
|
||||
if (strcmp(xNode->name(), "layer") == 0)
|
||||
ReadLayer(xNode);
|
||||
if (graphicsName.empty())
|
||||
return Error::NO_LAYERS;
|
||||
else
|
||||
if (strcmp(xNode->name(), "tileset") == 0)
|
||||
ReadTileset(xNode);
|
||||
else
|
||||
if (strcmp(xNode->name(), "objectgroup") == 0)
|
||||
ReadObjects(xNode);
|
||||
return Error::GRAPHICS_NOTFOUND;
|
||||
}
|
||||
if (layerCls == std::nullopt && !collisionName.empty())
|
||||
return Error::GRAPHICS_NOTFOUND;
|
||||
if (layerPal == std::nullopt && !paletteName.empty())
|
||||
return Error::PALETTE_NOTFOUND;
|
||||
|
||||
// Read TMX map
|
||||
mSize = Size { map.getTileCount().x, map.getTileCount().y };
|
||||
size_t numTiles = static_cast<size_t>(mSize.width) * static_cast<size_t>(mSize.height);
|
||||
|
||||
// Read graphics layer
|
||||
mGraphics.reserve(numTiles);
|
||||
for (auto tmxTile : layerGfx.value().get().getTiles())
|
||||
mGraphics.emplace_back(Tile { tmxTile.ID, tmxTile.flipFlags });
|
||||
|
||||
// Read optional layers
|
||||
if (layerPal != std::nullopt)
|
||||
{
|
||||
std::vector<uint32_t> v;
|
||||
v.reserve(numTiles);
|
||||
for (auto tmxTile : layerPal.value().get().getTiles())
|
||||
v.emplace_back(tmxTile.ID);
|
||||
mPalette.emplace(v);
|
||||
}
|
||||
if (layerCls != std::nullopt)
|
||||
{
|
||||
std::vector<uint32_t> v;
|
||||
v.reserve(numTiles);
|
||||
for (auto tmxTile : layerCls.value().get().getTiles())
|
||||
v.emplace_back(tmxTile.ID);
|
||||
mCollision.emplace(v);
|
||||
}
|
||||
|
||||
// Generate global id table
|
||||
for (auto tileset : mTilesets)
|
||||
mGidTable.push_back(tileset->GetFirstGid());
|
||||
std::sort(mGidTable.rbegin(), mGidTable.rend());
|
||||
}
|
||||
// Read tilesets
|
||||
const auto& tilesets = map.getTilesets();
|
||||
mGidTable.reserve(tilesets.size());
|
||||
for (const auto& set : tilesets)
|
||||
mGidTable.emplace_back(std::make_pair(set.getFirstGID(), set.getLastGID()));
|
||||
|
||||
const TmxLayer* TmxReader::GetLayer(const std::string& aName) const
|
||||
{
|
||||
for (auto layer : mLayers)
|
||||
// Read objects
|
||||
if (!objMapping.empty())
|
||||
{
|
||||
if (layer->GetName() == aName)
|
||||
return layer;
|
||||
std::vector<Object> v;
|
||||
for (const auto& group : objGroups)
|
||||
{
|
||||
const auto& tmxObjects = group.get().getObjects();
|
||||
v.reserve(v.size() + tmxObjects.size());
|
||||
for (const auto& tmxObj : tmxObjects)
|
||||
{
|
||||
auto it = objMapping.find(tmxObj.getName());
|
||||
if (it == objMapping.end())
|
||||
continue;
|
||||
|
||||
const auto& aabb = tmxObj.getAABB();
|
||||
Object obj;
|
||||
obj.id = it->second;
|
||||
obj.x = aabb.left;
|
||||
obj.y = aabb.top;
|
||||
|
||||
v.emplace_back(obj);
|
||||
}
|
||||
}
|
||||
mObjects.emplace(v);
|
||||
}
|
||||
return nullptr;
|
||||
|
||||
return Error::OK;
|
||||
}
|
||||
|
||||
uint32_t TmxReader::LidFromGid(uint32_t aGid)
|
||||
uint32_t TmxReader::LidFromGid(uint32_t aGid) const
|
||||
{
|
||||
for (uint32_t first : mGidTable)
|
||||
for (auto range : mGidTable)
|
||||
{
|
||||
if (first <= aGid)
|
||||
return aGid - (first - 1);
|
||||
if (aGid >= range.first && aGid <= range.second)
|
||||
return aGid - (range.first - 1);
|
||||
}
|
||||
return aGid;
|
||||
}
|
||||
|
||||
@@ -1,50 +1,80 @@
|
||||
/* 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
|
||||
|
||||
#include <istream>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <cstdint>
|
||||
#include <rapidxml/rapidxml.hpp>
|
||||
|
||||
class TmxTileset;
|
||||
class TmxLayer;
|
||||
class TmxObject;
|
||||
#include <span>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <optional>
|
||||
|
||||
class TmxReader
|
||||
{
|
||||
public:
|
||||
TmxReader() = default;
|
||||
~TmxReader();
|
||||
static constexpr uint8_t FLIP_HORZ = 0x8;
|
||||
static constexpr uint8_t FLIP_VERT = 0x4;
|
||||
static constexpr uint8_t FLIP_DIAG = 0x2;
|
||||
static constexpr uint8_t FLIP_MASK = 0xE;
|
||||
|
||||
void Open(std::istream& aIn);
|
||||
enum class Error
|
||||
{
|
||||
OK,
|
||||
LOAD_FAILED,
|
||||
NO_LAYERS,
|
||||
GRAPHICS_NOTFOUND,
|
||||
PALETTE_NOTFOUND,
|
||||
COLLISION_NOTFOUND
|
||||
};
|
||||
|
||||
constexpr int GetWidth() const { return mWidth; }
|
||||
constexpr int GetHeight() const { return mHeight; }
|
||||
[[nodiscard]] Error Open(const std::string& inPath,
|
||||
const std::string_view graphicsName,
|
||||
const std::string_view paletteName,
|
||||
const std::string_view collisionName,
|
||||
const std::map<std::string, uint32_t>& objMapping);
|
||||
struct Size { unsigned width, height; };
|
||||
|
||||
inline const TmxLayer* GetLayer(size_t aId) const { return mLayers.at(aId); }
|
||||
const TmxLayer* GetLayer(const std::string& aName) const;
|
||||
inline std::size_t GetLayerCount() const { return mLayers.size(); }
|
||||
[[nodiscard]] constexpr Size GetSize() const { return mSize; }
|
||||
[[nodiscard]] constexpr size_t TileCount() const { return
|
||||
static_cast<size_t>(mSize.width) *
|
||||
static_cast<size_t>(mSize.height); }
|
||||
|
||||
inline const TmxObject* GetObject(size_t aId) const { return mObjects.at(aId); }
|
||||
inline size_t GetObjectCount() const { return mObjects.size(); }
|
||||
[[nodiscard]] uint32_t LidFromGid(uint32_t aGid) const;
|
||||
|
||||
uint32_t LidFromGid(uint32_t aGid);
|
||||
struct Tile { uint32_t id; uint8_t flags; };
|
||||
struct Object { unsigned id; float x, y; };
|
||||
|
||||
[[nodiscard]] constexpr bool HasCollisionTiles() const { return mCollision.has_value(); }
|
||||
[[nodiscard]] constexpr bool HasObjects() const { return mObjects.has_value(); }
|
||||
|
||||
[[nodiscard]] constexpr const std::span<const Tile> GetGraphicsTiles() const { return mGraphics; }
|
||||
[[nodiscard]] constexpr const std::optional<std::span<const uint32_t>> GetPaletteTiles() const
|
||||
{
|
||||
if (mPalette.has_value()) { return { mPalette.value() }; }
|
||||
return std::nullopt;
|
||||
}
|
||||
[[nodiscard]] constexpr const std::optional<std::span<const uint32_t>> GetCollisionTiles() const
|
||||
{
|
||||
if (mCollision.has_value()) { return { mCollision.value() }; }
|
||||
return std::nullopt;
|
||||
}
|
||||
[[nodiscard]] constexpr const std::optional<std::span<const Object>> GetObjects() const
|
||||
{
|
||||
if (mObjects.has_value()) { return { mObjects.value() }; }
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
private:
|
||||
static bool DecodeMap(uint32_t* aOut, size_t aOutSize, const std::string& aBase64Dat);
|
||||
void ReadTileset(rapidxml::xml_node<>* aXNode);
|
||||
void ReadLayer(rapidxml::xml_node<>* aXNode);
|
||||
void ReadObjects(rapidxml::xml_node<>* aXNode);
|
||||
Size mSize;
|
||||
|
||||
int mWidth, mHeight;
|
||||
std::vector<TmxTileset*> mTilesets;
|
||||
std::vector<TmxLayer*> mLayers;
|
||||
std::vector<TmxObject*> mObjects;
|
||||
std::vector<uint32_t> mGidTable;
|
||||
std::vector<std::pair<uint32_t, uint32_t>> mGidTable;
|
||||
|
||||
std::vector<Tile> mGraphics;
|
||||
std::optional<std::vector<uint32_t>> mPalette;
|
||||
std::optional<std::vector<uint32_t>> mCollision;
|
||||
std::optional<std::vector<Object>> mObjects;
|
||||
};
|
||||
|
||||
#endif//TMXREADER_HPP
|
||||
|
||||
@@ -1,28 +0,0 @@
|
||||
/* tmxtileset.hpp - Copyright (C) 2015-2022 a dinosaur (zlib, see COPYING.txt) */
|
||||
|
||||
#ifndef TMXTILESET_HPP
|
||||
#define TMXTILESET_HPP
|
||||
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <utility>
|
||||
|
||||
class TmxTileset
|
||||
{
|
||||
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;
|
||||
|
||||
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;
|
||||
};
|
||||
|
||||
#endif//TMXTILESET_HPP
|
||||
Reference in New Issue
Block a user