1
0
mirror of https://github.com/ScrelliCopter/tmx2gba.git synced 2025-02-21 03:29:25 +11:00
This commit is contained in:
2022-09-07 02:34:09 +10:00
parent 6c2cca3fd5
commit a5ac1d9bfe
12 changed files with 470 additions and 735 deletions

View File

@@ -1,9 +1,9 @@
add_executable(tmx2gba
tmx2gba.cpp
tmxlayer.cpp tmxlayer.h
tmxobject.cpp tmxobject.h
tmxreader.cpp tmxreader.h
tmxtileset.cpp tmxtileset.h)
tmxlayer.hpp
tmxobject.hpp
tmxreader.hpp tmxreader.cpp
tmxtileset.hpp)
target_link_libraries(tmx2gba
External::base64
External::miniz

View File

@@ -20,9 +20,9 @@
*/
#include "tmxreader.h"
#include "tmxlayer.h"
#include "tmxobject.h"
#include "tmxreader.hpp"
#include "tmxlayer.hpp"
#include "tmxobject.hpp"
#include <iostream>
#include <iomanip>
#include <fstream>
@@ -34,22 +34,9 @@
#include <ultragetopt.h>
using std::cout;
using std::cerr;
using std::endl;
using std::string;
using std::vector;
using std::map;
using std::ifstream;
using std::ofstream;
using std::stoi;
using std::min;
using std::max;
using std::ios;
static const string g_strUsage = "Usage: tmx2gba [-h] [-f file] [-r offset] [-lyc name] [-p 0-15] [-m name;id] <-i inpath> <-o outpath>";
static const string g_strHelpShort = "Run 'tmx2gba -h' to view all available options.";
static const string g_strHelpFull = R"(
const std::string helpUsage = "Usage: tmx2gba [-h] [-f file] [-r offset] [-lyc name] [-p 0-15] [-m name;id] <-i inpath> <-o outpath>";
const std::string helpShort = "Run 'tmx2gba -h' to view all available options.";
const std::string helpFull = R"(
-h ------------ Display this help & command info.
-l <name> ----- Name of layer to use (default first layer in TMX).
-y <name> ----- Layer for palette mappings.
@@ -61,66 +48,44 @@ static const string g_strHelpFull = R"(
-o <path> ----- Path to output files.
-f <file> ----- Specify a file to use for flags, will override any options specified on the command line.)";
struct SParams
struct Arguments
{
bool help = false;
string inPath, outPath;
string layer, collisionlay, paletteLay;
string flagFile;
std::string inPath, outPath;
std::string layer, collisionlay, paletteLay;
std::string flagFile;
int offset = 0;
int palette = 0;
vector<string> objMappings;
std::vector<std::string> objMappings;
bool objExport = false;
};
void ParseArgs ( int argc, char** argv, SParams* params )
void ParseArgs(int argc, char** argv, Arguments& p)
{
char cOption;
int opt;
optreset = 1;
while ( ( cOption = (char)getopt ( argc, argv, "hr:l:c:p:y:m:i:o:f:" ) ) > 0 )
while ((opt = getopt(argc, argv, "hr:l:c:p:y:m:i:o:f:")) > 0)
{
switch ( cOption )
switch (opt)
{
case ( 'h' ):
params->help = true;
case ('h'):
p.help = true;
return;
case ( 'l' ):
params->layer = optarg;
case ('l'): p.layer = optarg; break;
case ('c'): p.collisionlay = optarg; break;
case ('y'): p.paletteLay = optarg; break;
case ('r'): p.offset = std::stoi(optarg); break;
case ('p'): p.palette = std::stoi(optarg); break;
case ('m'):
p.objExport = true;
p.objMappings.emplace_back(optarg);
break;
case ( 'c' ):
params->collisionlay = optarg;
break;
case ( 'y' ):
params->paletteLay = optarg;
break;
case ( 'r' ):
params->offset = stoi ( optarg );
break;
case ( 'p' ):
params->palette = stoi ( optarg );
break;
case ( 'm' ):
params->objExport = true;
params->objMappings.push_back ( optarg );
break;
case ( 'i' ):
params->inPath = optarg;
break;
case ( 'o' ):
params->outPath = optarg;
break;
case ( 'f' ):
params->flagFile = optarg;
break;
case ('i'): p.inPath = optarg; break;
case ('o'): p.outPath = optarg; break;
case ('f'): p.flagFile = optarg; break;
default:
break;
@@ -128,25 +93,25 @@ void ParseArgs ( int argc, char** argv, SParams* params )
}
}
bool CheckArgs ( const SParams* params )
bool CheckArgs(const Arguments& params)
{
// Check my paranoia.
if ( params->inPath.empty () )
if (params.inPath.empty())
{
cerr << "No input file specified." << endl;
cout << g_strUsage << endl << g_strHelpShort << endl;
std::cerr << "No input file specified." << std::endl;
std::cout << helpUsage << std::endl << helpShort << std::endl;
return false;
}
if ( params->outPath.empty () )
if (params.outPath.empty())
{
cerr << "No output file specified." << endl;
cout << g_strUsage << endl << g_strHelpShort << endl;
std::cerr << "No output file specified." << std::endl;
std::cout << helpUsage << std::endl << helpShort << std::endl;
return false;
}
if ( params->palette < 0 || params->palette > 15 )
if (params.palette < 0 || params.palette > 15)
{
cerr << "Invalid palette index." << endl;
cout << g_strUsage << endl << g_strHelpShort << endl;
std::cerr << "Invalid palette index." << std::endl;
std::cout << helpUsage << std::endl << helpShort << std::endl;
return false;
}
@@ -154,50 +119,36 @@ bool CheckArgs ( const SParams* params )
}
template <typename T> constexpr const char* DatType();
template <> constexpr const char* DatType<uint8_t>() { return ".byte"; }
template <> constexpr const char* DatType<uint16_t>() { return ".hword"; }
template <> constexpr const char* DatType<uint32_t>() { return ".word"; }
template <typename T>
void WriteArray ( ofstream& a_fout, const vector<T>& a_dat, int a_perCol = 16 )
void WriteArray(std::ofstream& aOut, const std::vector<T>& aDat, int aPerCol = 16)
{
const int w = sizeof(T) * 2;
int col = 0;
string datType = "ERR";
if ( sizeof(T) == 1 )
{
datType = ".byte";
}
else
if ( sizeof(T) == 2 )
{
datType = ".hword";
}
else
if ( sizeof(T) == 4 )
{
datType = ".word";
}
a_fout.setf ( ios::hex, ios::basefield );
a_fout.setf ( ios::showbase );
aOut.setf(std::ios::hex, std::ios::basefield);
aOut.setf(std::ios::showbase);
size_t i = 0;
for ( T element : a_dat )
for (T element : aDat)
{
if ( col == 0 )
{
a_fout << "\t" << datType << " ";
}
if (col == 0)
aOut << "\t" << DatType<T>() << " ";
a_fout << std::hex << (int)element;
aOut << std::hex << (int)element;
if ( i < a_dat.size () - 1 )
if (i < aDat.size() - 1)
{
if ( ++col < a_perCol )
if (++col < aPerCol)
{
a_fout << ",";
aOut << ",";
}
else
{
a_fout << "" << endl;
aOut << "" << std::endl;
col = 0;
}
}
@@ -206,292 +157,282 @@ void WriteArray ( ofstream& a_fout, const vector<T>& a_dat, int a_perCol = 16 )
}
}
int main ( int argc, char** argv )
int main(int argc, char** argv)
{
SParams params;
ParseArgs ( argc, argv, &params );
Arguments p;
ParseArgs(argc, argv, p);
if ( params.help )
if (p.help)
{
cout << g_strUsage << endl << g_strHelpFull << endl;
std::cout << helpUsage << std::endl << helpFull << std::endl;
return 0;
}
if ( params.flagFile.length () != 0 )
if (!p.flagFile.empty())
{
ifstream paramFile ( params.flagFile );
if ( !paramFile.is_open () )
std::ifstream paramFile(p.flagFile);
if (!paramFile.is_open())
{
cerr << "Failed to open param file." << endl;
std::cerr << "Failed to open param file." << std::endl;
return -1;
}
vector<string> fileArgTokens;
fileArgTokens.push_back ( "auu~~" );
std::vector<std::string> fileArgTokens;
fileArgTokens.push_back("auu~~");
bool carry = false;
string token;
while ( !paramFile.eof () )
std::string rawToken;
while (!paramFile.eof())
{
if ( carry )
if (carry)
{
string tmp;
std::string tmp;
paramFile >> tmp;
token += " ";
token += tmp;
rawToken += " ";
rawToken += tmp;
}
else
{
token.clear ();
paramFile >> token;
rawToken.clear();
paramFile >> rawToken;
}
if ( token == "" )
{
if (rawToken.empty())
continue;
}
bool qFr = token[0] == '"';
bool qBk = token[token.length () - 1] == '"';
if ( qFr && qBk )
bool qFr = rawToken[0] == '"';
bool qBk = rawToken[rawToken.length() - 1] == '"';
if (qFr && qBk)
{
fileArgTokens.push_back ( token.substr ( 1, token.length () - 2 ) );
fileArgTokens.push_back(rawToken.substr(1, rawToken.length() - 2));
}
else
if ( qFr )
if (qFr)
{
fileArgTokens.push_back ( token.substr ( 1, token.length () - 1 ) );
fileArgTokens.push_back(rawToken.substr(1, rawToken.length() - 1));
carry = true;
}
else
if ( qBk )
if (qBk)
{
fileArgTokens.push_back ( token.substr ( 0, token.length () - 1 ) );
fileArgTokens.push_back(rawToken.substr(0, rawToken.length() - 1));
carry = false;
}
else
{
fileArgTokens.push_back ( token );
fileArgTokens.push_back(rawToken);
}
}
vector<const char*> fileArgs;
fileArgs.reserve ( fileArgTokens.size () );
for ( auto& token : fileArgTokens )
{
fileArgs.push_back ( token.c_str () );
}
std::vector<const char*> fileArgs;
fileArgs.reserve(fileArgTokens.size());
for (const auto& token : fileArgTokens)
fileArgs.push_back(token.c_str());
fileArgs.push_back(nullptr);
ParseArgs ( fileArgs.size () - 1, (char**)fileArgs.data (), &params );
ParseArgs(static_cast<int>(fileArgs.size()) - 1, (char**)fileArgs.data(), p);
}
if ( !CheckArgs ( &params ) )
{
if (!CheckArgs(p))
return -1;
}
// Object mappings.
map<string, uint32_t> objMapping;
if ( params.objExport )
std::map<std::string, uint32_t> objMapping;
if (p.objExport)
{
for ( auto token : params.objMappings )
for (const auto& objToken : p.objMappings)
{
int splitter = token.find_last_of ( ';' );
if ( splitter == -1 )
auto splitter = objToken.find_last_of(';');
if (splitter == std::string::npos)
{
cerr << "Malformed mapping (missing a splitter)." << endl;
std::cerr << "Malformed mapping (missing a splitter)." << std::endl;
return -1;
}
try
{
string name = token.substr ( 0, splitter );
int id = stoi ( token.substr ( splitter + 1 ) );
std::string name = objToken.substr(0, splitter);
int id = std::stoi(objToken.substr(splitter + 1));
objMapping[name] = id;
}
catch ( std::exception )
catch (std::exception&)
{
cerr << "Malformed mapping, make sure id is numeric." << endl;
std::cerr << "Malformed mapping, make sure id is numeric." << std::endl;
}
}
}
// Open & read input file.
CTmxReader tmx;
ifstream fin ( params.inPath );
if ( !fin.is_open () )
TmxReader tmx;
std::ifstream fin(p.inPath);
if (!fin.is_open())
{
cerr << "Failed to open input file." << endl;
std::cerr << "Failed to open input file." << std::endl;
return -1;
}
tmx.Open ( fin );
tmx.Open(fin);
// Get layers.
if ( tmx.GetLayerCount () == 0 )
if (tmx.GetLayerCount() == 0)
{
cerr << "No layers found." << endl;
std::cerr << "No layers found." << std::endl;
return -1;
}
const CTmxLayer* pLayerGfx = params.layer.empty () ? tmx.GetLayer ( 0 ) : tmx.GetLayer ( params.layer );
const CTmxLayer* pLayerCls = params.collisionlay.empty () ? nullptr : tmx.GetLayer ( params.collisionlay );
const CTmxLayer* pLayerPal = params.paletteLay.empty () ? nullptr : tmx.GetLayer ( params.paletteLay );
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 ( pLayerGfx == nullptr )
if (layerGfx == nullptr)
{
cerr << "Input layer not found." << endl;
std::cerr << "Input layer not found." << std::endl;
return -1;
}
// Open output files.
ofstream foutS ( params.outPath + ".s" );
ofstream foutH ( params.outPath + ".h" );
if ( !foutS.is_open () || !foutH.is_open () )
std::ofstream foutS(p.outPath + ".s");
std::ofstream foutH(p.outPath + ".h");
if (!foutS.is_open() || !foutH.is_open())
{
cerr << "Failed to create output file(s).";
std::cerr << "Failed to create output file(s).";
return -1;
}
int slashPos = max ( (int)params.outPath.find_last_of ( '/' ), (int)params.outPath.find_last_of ( '\\' ) );
string name = params.outPath;
if ( slashPos != -1 )
{
name = name.substr ( slashPos + 1 );
}
int slashPos = std::max((int)p.outPath.find_last_of('/'), (int)p.outPath.find_last_of('\\'));
std::string name = p.outPath;
if (slashPos != -1)
name = name.substr(slashPos + 1);
// Write header guards.
string guard = "__TMX2GBA_" + name + "__";
for ( auto& c: guard ) c = (char)toupper ( (int)c );
foutH << "#ifndef " << guard << endl;
foutH << "#define " << guard << endl;
foutH << endl;
foutH << "#define " << name << "Width " << tmx.GetWidth () << endl;
foutH << "#define " << name << "Height " << tmx.GetHeight () << endl;
foutH << endl;
std::string guard = "TMX2GBA_" + name;
for (auto& c: guard)
c = static_cast<char>(toupper(c));
foutH << "#ifndef " << guard << std::endl;
foutH << "#define " << guard << std::endl;
foutH << std::endl;
foutH << "#define " << name << "Width " << tmx.GetWidth() << std::endl;
foutH << "#define " << name << "Height " << tmx.GetHeight() << std::endl;
foutH << std::endl;
// Convert to GBA-friendly charmap data.
const uint32_t* pRead = pLayerGfx->GetData ();
const uint32_t* pPalRead = pLayerPal == nullptr ? nullptr : pLayerPal->GetData ();
vector<uint16_t> vucCharDat;
vucCharDat.reserve ( pLayerGfx->GetWidth () * pLayerGfx->GetHeight () );
for ( size_t i = 0; i < size_t(pLayerGfx->GetWidth () * pLayerGfx->GetHeight ()); ++i )
const uint32_t* gfxTiles = layerGfx->GetData();
const uint32_t* palTiles = (layerPal == nullptr) ? nullptr : layerPal->GetData();
std::vector<uint16_t> charDat;
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 uiRead = (*pRead++);
uint32_t read = (*gfxTiles++);
uint16_t usTile = (uint16_t)max<int32_t> ( 0, tmx.LidFromGid ( uiRead & ~FLIP_MASK ) + params.offset );
uint8_t ucFlags = 0x0;
uint16_t tile = (uint16_t)std::max<int32_t>(0, tmx.LidFromGid(read & ~TmxLayer::FLIP_MASK) + p.offset);
uint8_t flags = 0x0;
// Get flipped!
ucFlags |= ( uiRead & FLIP_HORZ ) ? 0x4 : 0x0;
ucFlags |= ( uiRead & FLIP_VERT ) ? 0x8 : 0x0;
flags |= (read & TmxLayer::FLIP_HORZ) ? 0x4 : 0x0;
flags |= (read & TmxLayer::FLIP_VERT) ? 0x8 : 0x0;
// Determine palette ID.
uint32_t uiIndex = 0;
if ( pPalRead != nullptr )
{
uiIndex = tmx.LidFromGid ( (*pPalRead++) & ~FLIP_MASK );
}
if ( uiIndex == 0 )
{
uiIndex = params.palette + 1;
}
ucFlags |= (uint8_t)(uiIndex - 1) << 4;
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;
vucCharDat.push_back ( usTile | ( uint16_t(ucFlags) << 8 ) );
charDat.push_back(tile | (static_cast<uint16_t>(flags) << 8));
}
// Save out charmap.
foutH << "#define " << name << "TilesLen " << vucCharDat.size () * 2 << endl;
foutH << "extern const unsigned short " << name << "Tiles[" << vucCharDat.size () << "];" << endl;
foutH << endl;
foutH << "#define " << name << "TilesLen " << charDat.size() * 2 << std::endl;
foutH << "extern const unsigned short " << name << "Tiles[" << charDat.size() << "];" << std::endl;
foutH << std::endl;
foutS << "\t.section .rodata" << endl;
foutS << "\t.align 2" << endl;
foutS << "\t.global " << name << "Tiles" << endl;
foutS << "\t.hidden " << name << "Tiles" << endl;
foutS << name << "Tiles" << ":" << endl;
WriteArray<uint16_t> ( foutS, vucCharDat );
foutS << endl;
foutS << "\t.section .rodata" << std::endl;
foutS << "\t.align 2" << std::endl;
foutS << "\t.global " << name << "Tiles" << std::endl;
foutS << "\t.hidden " << name << "Tiles" << std::endl;
foutS << name << "Tiles" << ":" << std::endl;
WriteArray<uint16_t>(foutS, charDat);
foutS << std::endl;
// Convert collision map & save it out.
if ( pLayerCls != nullptr )
if (layerCls != nullptr)
{
vector<uint8_t> vucCollisionDat;
vucCollisionDat.reserve ( pLayerCls->GetWidth () * pLayerCls->GetHeight () );
std::vector<uint8_t> vucCollisionDat;
vucCollisionDat.reserve(layerCls->GetWidth() * layerCls->GetHeight());
pRead = pLayerCls->GetData ();
for ( int i = 0; i < pLayerCls->GetWidth () * pLayerCls->GetHeight (); ++i )
gfxTiles = layerCls->GetData();
for (int i = 0; i < layerCls->GetWidth() * layerCls->GetHeight(); ++i)
{
uint8_t ucTile = (uint8_t)tmx.LidFromGid ( (*pRead++) & ~FLIP_MASK );
vucCollisionDat.push_back ( ucTile );
uint8_t ucTile = (uint8_t)tmx.LidFromGid((*gfxTiles++) & ~TmxLayer::FLIP_MASK);
vucCollisionDat.push_back(ucTile);
}
// Try to nicely append "_collision" to the output name.
string strPath;
size_t extPos = params.outPath.find_last_of ( '.' );
if ( extPos != string::npos )
{
strPath = params.outPath.insert ( extPos, "_collision" );
}
std::string path;
size_t extPos = p.outPath.find_last_of('.');
if (extPos != std::string::npos)
path = p.outPath.insert(extPos, "_collision");
else
{
strPath = params.outPath + "_collision";
}
path = p.outPath + "_collision";
// Save it out.
foutH << "#define " << name << "CollisionLen " << vucCollisionDat.size () << endl;
foutH << "extern const unsigned char " << name << "Collision[" << vucCollisionDat.size () << "];" << endl;
foutH << endl;
foutH << "#define " << name << "CollisionLen " << vucCollisionDat.size() << std::endl;
foutH << "extern const unsigned char " << name << "Collision[" << vucCollisionDat.size() << "];" << std::endl;
foutH << std::endl;
foutS << endl;
foutS << "\t.section .rodata" << endl;
foutS << "\t.align 2" << endl;
foutS << "\t.global " << name << "Collision" << endl;
foutS << "\t.hidden " << name << "Collision" << endl;
foutS << name << "Collision" << ":" << endl;
WriteArray<uint8_t> ( foutS, vucCollisionDat );
foutS << endl;
foutS << std::endl;
foutS << "\t.section .rodata" << std::endl;
foutS << "\t.align 2" << std::endl;
foutS << "\t.global " << name << "Collision" << std::endl;
foutS << "\t.hidden " << name << "Collision" << std::endl;
foutS << name << "Collision" << ":" << std::endl;
WriteArray<uint8_t>(foutS, vucCollisionDat);
foutS << std::endl;
}
if ( params.objExport )
if (p.objExport)
{
vector<uint32_t> objDat;
for ( int i = 0; i < tmx.GetObjectCount (); ++i )
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 () )
{
auto obj = tmx.GetObject(i);
auto it = objMapping.find(obj->GetName());
if (it == objMapping.end())
continue;
}
float x, y;
obj->GetPos ( &x, &y );
objDat.push_back ( it->second );
objDat.push_back ( (int)( x * 256.0f ) );
objDat.push_back ( (int)( y * 256.0f ) );
obj->GetPos(x, y);
objDat.push_back(it->second);
objDat.push_back((int)(x * 256.0f));
objDat.push_back((int)(y * 256.0f));
}
// Save it out.
foutH << "#define " << name << "ObjCount " << objDat.size () / 3 << endl;
foutH << "#define " << name << "ObjdatLen " << objDat.size () * sizeof(int) << endl;
foutH << "extern const unsigned int " << name << "Objdat[" << objDat.size () << "];" << endl;
foutH << endl;
foutH << "#define " << name << "ObjCount " << objDat.size() / 3 << std::endl;
foutH << "#define " << name << "ObjdatLen " << objDat.size() * sizeof(int) << std::endl;
foutH << "extern const unsigned int " << name << "Objdat[" << objDat.size() << "];" << std::endl;
foutH << std::endl;
foutS << endl;
foutS << "\t.section .rodata" << endl;
foutS << "\t.align 2" << endl;
foutS << "\t.global " << name << "Objdat" << endl;
foutS << "\t.hidden " << name << "Objdat" << endl;
foutS << name << "Objdat" << ":" << endl;
WriteArray<uint32_t> ( foutS, objDat );
foutS << endl;
foutS << std::endl;
foutS << "\t.section .rodata" << std::endl;
foutS << "\t.align 2" << std::endl;
foutS << "\t.global " << name << "Objdat" << std::endl;
foutS << "\t.hidden " << name << "Objdat" << std::endl;
foutS << name << "Objdat" << ":" << std::endl;
WriteArray<uint32_t>(foutS, objDat);
foutS << std::endl;
}
foutH << "#endif//" << guard << endl;
foutH << "#endif//" << guard << std::endl;
foutH.close ();
foutS.close ();
foutH.close();
foutS.close();
return 0;
}

View File

@@ -1,68 +0,0 @@
/* tmxlayer.cpp
Copyright (C) 2015 a dinosaur
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.
*/
#include "tmxlayer.h"
CTmxLayer::CTmxLayer () :
m_iWidth ( 0 ),
m_iHeight ( 0 ),
m_strName ( "" ),
m_pTileDat ( nullptr )
{
}
CTmxLayer::CTmxLayer ( int a_iWidth, int a_iHeight, const char* a_szName, uint32_t* a_pTileDat ) :
m_iWidth ( a_iWidth ),
m_iHeight ( a_iHeight ),
m_strName ( a_szName ),
m_pTileDat ( a_pTileDat )
{
}
CTmxLayer::~CTmxLayer ()
{
delete[] m_pTileDat;
}
const std::string& CTmxLayer::GetName () const
{
return m_strName;
}
int CTmxLayer::GetWidth () const
{
return m_iWidth;
}
int CTmxLayer::GetHeight () const
{
return m_iHeight;
}
const uint32_t* CTmxLayer::GetData () const
{
return m_pTileDat;
}

View File

@@ -1,31 +0,0 @@
#ifndef __TMXLAYER_H__
#define __TMXLAYER_H__
#include <string>
#include <cstdint>
const uint32_t FLIP_HORZ = 0x80000000;
const uint32_t FLIP_VERT = 0x40000000;
const uint32_t FLIP_DIAG = 0x20000000;
const uint32_t FLIP_MASK = 0xE0000000;
class CTmxLayer
{
public:
CTmxLayer ();
CTmxLayer ( int a_iWidth, int a_iHeight, const char* a_szName, uint32_t* a_pTileDat );
~CTmxLayer ();
const std::string& GetName () const;
int GetWidth () const;
int GetHeight () const;
const uint32_t* GetData () const;
private:
std::string m_strName;
int m_iWidth, m_iHeight;
uint32_t* m_pTileDat;
};
#endif//__TMXLAYER_H__

54
src/tmxlayer.hpp Normal file
View File

@@ -0,0 +1,54 @@
/* tmxlayer.cpp
Copyright (C) 2015-2022 a dinosaur
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 TMXLAYER_H
#define TMXLAYER_H
#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_H

View File

@@ -1,21 +0,0 @@
#ifndef __TMXOBJECT_H__
#define __TMXOBJECT_H__
#include <string>
class CTmxObject
{
public:
CTmxObject ();
CTmxObject ( const std::string& a_name, float a_x, float a_y );
~CTmxObject ();
const std::string& GetName () const;
void GetPos ( float* a_outX, float* a_outY ) const;
private:
std::string m_name;
float m_x, m_y;
};
#endif//__TMXOBJECT_H__

View File

@@ -1,6 +1,6 @@
/* tmxobject.cpp
Copyright (C) 2015 a dinosaur
Copyright (C) 2015-2022 a dinosaur
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -20,42 +20,26 @@
*/
#include "tmxobject.h"
#ifndef TMXOBJECT_H
#define TMXOBJECT_H
CTmxObject::CTmxObject () :
m_name ( "" ),
m_x ( 0.0f ), m_y ( 0.0f )
#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; }
CTmxObject::CTmxObject ( const std::string& a_name, float a_x, float a_y ) :
m_name ( a_name ),
m_x ( a_x ), m_y ( a_y )
{
}
CTmxObject::~CTmxObject ()
{
}
const std::string& CTmxObject::GetName () const
{
return m_name;
}
void CTmxObject::GetPos ( float* a_outX, float* a_outY ) const
{
if ( a_outX != nullptr )
{
*a_outX = m_x;
}
if ( a_outY != nullptr )
{
*a_outY = m_y;
}
}
private:
std::string mName;
float mX, mY;
};
#endif//TMXOBJECT_H

View File

@@ -1,6 +1,6 @@
/* tmxreader.cpp
Copyright (C) 2015 a dinosaur
Copyright (C) 2015-2022 a dinosaur
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -20,10 +20,10 @@
*/
#include "tmxreader.h"
#include "tmxtileset.h"
#include "tmxobject.h"
#include "tmxlayer.h"
#include "tmxreader.hpp"
#include "tmxtileset.hpp"
#include "tmxobject.hpp"
#include "tmxlayer.hpp"
#include <cstdint>
#include <sstream>
#include <algorithm>
@@ -32,296 +32,208 @@
#include <miniz.h>
CTmxReader::CTmxReader ()
{
}
CTmxReader::~CTmxReader ()
TmxReader::~TmxReader()
{
// Delete old tilesets.
for ( auto pTileset : m_tileset )
{
for (auto pTileset : mTilesets)
delete pTileset;
}
m_tileset.clear ();
mTilesets.clear();
// Delete old layers.
for ( auto pLay : m_layers )
{
for (auto pLay : mLayers)
delete pLay;
}
m_layers.clear ();
mLayers.clear();
}
bool CTmxReader::DecodeMap ( uint32_t* a_pOut, size_t a_outSize, const std::string& a_strIn )
bool TmxReader::DecodeMap(uint32_t* aOut, size_t aOutSize, const std::string& aStrIn)
{
// Decode base64 string.
size_t cutTheCrap = a_strIn.find_first_not_of ( " \t\n\r" );
std::string strDec = base64_decode ( a_strIn.substr ( cutTheCrap ) );
size_t cutTheCrap = aStrIn.find_first_not_of(" \t\n\r");
std::string decoded = base64_decode(aStrIn.substr(cutTheCrap));
// Decompress compressed data.
mz_ulong uiDstSize = a_outSize;
int iRes = uncompress (
(unsigned char*)a_pOut, &uiDstSize,
(const unsigned char*)strDec.data (), strDec.size ()
);
strDec.clear ();
if ( iRes < 0 )
{
auto dstSize = static_cast<mz_ulong>(aOutSize);
int res = uncompress(
(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 CTmxReader::ReadTileset ( rapidxml::xml_node<>* a_xNode )
void TmxReader::ReadTileset(rapidxml::xml_node<>* aXNode)
{
rapidxml::xml_attribute<>* xAttrib;
const char* szName = "";
const char* szSource = "";
uint32_t uiFirstGid = 0;
const char* name = "";
const char* source = "";
uint32_t firstGid = 0;
// Read name.
xAttrib = a_xNode->first_attribute ( "name" );
if ( xAttrib != nullptr )
{
szName = xAttrib->value ();
}
xAttrib = aXNode->first_attribute("name");
if (xAttrib != nullptr)
name = xAttrib->value();
// Read source.
xAttrib = a_xNode->first_attribute ( "source" );
if ( xAttrib != nullptr )
{
szSource = xAttrib->value ();
}
xAttrib = aXNode->first_attribute("source");
if (xAttrib != nullptr)
source = xAttrib->value();
// Read first global ID.
xAttrib = a_xNode->first_attribute ( "firstgid" );
if ( xAttrib != nullptr )
{
uiFirstGid = std::stoul ( xAttrib->value () );
}
xAttrib = aXNode->first_attribute("firstgid");
if (xAttrib != nullptr)
firstGid = std::stoul(xAttrib->value());
m_tileset.push_back ( new CTmxTileset ( szName, szSource, uiFirstGid ) );
mTilesets.push_back(new TmxTileset(name, source, firstGid));
}
void CTmxReader::ReadLayer ( rapidxml::xml_node<>* a_xNode )
void TmxReader::ReadLayer(rapidxml::xml_node<>* aXNode)
{
rapidxml::xml_attribute<>* xAttrib;
const char* szName = "";
int iWidth = 0;
int iHeight = 0;
uint32_t* pTileDat = nullptr;
const char* name = "";
int width = 0;
int height = 0;
uint32_t* tileDat = nullptr;
// Read name.
xAttrib = a_xNode->first_attribute ( "name" );
if ( xAttrib != nullptr )
{
szName = xAttrib->value ();
}
xAttrib = aXNode->first_attribute("name");
if (xAttrib != nullptr)
name = xAttrib->value();
// Read width.
xAttrib = a_xNode->first_attribute ( "width" );
if ( xAttrib != nullptr )
{
iWidth = std::stoi ( xAttrib->value () );
}
xAttrib = aXNode->first_attribute("width");
if (xAttrib != nullptr)
width = std::stoi(xAttrib->value());
// Read height.
xAttrib = a_xNode->first_attribute ( "height" );
if ( xAttrib != nullptr )
{
iHeight = std::stoi ( xAttrib->value () );
}
xAttrib = aXNode->first_attribute("height");
if (xAttrib != nullptr)
height = std::stoi(xAttrib->value());
// Read tile data.
auto xData = a_xNode->first_node ( "data" );
if ( xData != nullptr )
auto xData = aXNode->first_node("data");
if (xData != nullptr)
{
// TODO: don't assume base64 & zlib.
pTileDat = new uint32_t[iWidth * iHeight];
if ( !DecodeMap ( pTileDat, iWidth * iHeight * sizeof(uint32_t), std::string ( xData->value () ) ) )
tileDat = new uint32_t[width * height];
if (!DecodeMap(tileDat, width * height * sizeof(uint32_t), std::string(xData->value())))
{
delete[] pTileDat;
pTileDat = nullptr;
delete[] tileDat;
tileDat = nullptr;
}
}
m_layers.push_back ( new CTmxLayer ( iWidth, iHeight, szName, pTileDat ) );
mLayers.push_back(new TmxLayer(width, height, name, tileDat));
}
void CTmxReader::ReadObjects ( rapidxml::xml_node<>* a_xNode )
void TmxReader::ReadObjects(rapidxml::xml_node<>* aXNode)
{
for ( auto xNode = a_xNode->first_node (); xNode != nullptr; xNode = xNode->next_sibling () )
for (auto xNode = aXNode->first_node(); xNode != nullptr; xNode = xNode->next_sibling())
{
if ( strcmp ( xNode->name (), "object" ) != 0 )
{
if (strcmp(xNode->name(), "object") != 0)
continue;
}
rapidxml::xml_attribute<>* xAttrib;
const char* name = "";
float x = 0.0f;
float y = 0.0f;
const char* name = "";
float x = 0.0f;
float y = 0.0f;
// Read name.
xAttrib = xNode->first_attribute ( "name" );
if ( xAttrib != nullptr )
{
name = xAttrib->value ();
}
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 () );
}
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 () );
}
xAttrib = xNode->first_attribute("y");
if (xAttrib != nullptr)
y = std::stof(xAttrib->value());
m_objects.push_back ( new CTmxObject ( name, x, y ) );
mObjects.push_back(new TmxObject(name, x, y));
}
}
void CTmxReader::Open ( std::istream& a_in )
void TmxReader::Open(std::istream& aIn)
{
// Delete old tilesets.
for ( auto pTileset : m_tileset )
{
delete pTileset;
}
m_tileset.clear ();
for (auto tileset : mTilesets)
delete tileset;
mTilesets.clear();
// Delete old layers.
for ( auto pLay : m_layers )
{
delete pLay;
}
m_layers.clear ();
for (auto layer : mLayers)
delete layer;
mLayers.clear();
m_gidTable.clear ();
mGidTable.clear();
// Read string into a buffer.
std::stringstream buf;
buf << a_in.rdbuf ();
std::string strXml = buf.str ();
buf.clear ();
buf << aIn.rdbuf();
std::string strXml = buf.str();
buf.clear();
// Parse document.
rapidxml::xml_document<> xDoc;
xDoc.parse<0> ( (char*)strXml.c_str () );
xDoc.parse<0>((char*)strXml.c_str());
// Get map node.
auto xMap = xDoc.first_node ( "map" );
if ( xMap == nullptr )
{
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 )
{
m_width = std::stoi ( xAttrib->value () );
}
if ( ( xAttrib = xMap->first_attribute ( "height" ) ) != nullptr )
{
m_height = std::stoi ( xAttrib->value () );
}
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 () )
for (auto xNode = xMap->first_node(); xNode != nullptr; xNode = xNode->next_sibling())
{
// Read layer nodes.
if ( strcmp ( xNode->name (), "layer" ) == 0 )
{
ReadLayer ( xNode );
}
if (strcmp(xNode->name(), "layer") == 0)
ReadLayer(xNode);
else
if ( strcmp ( xNode->name (), "tileset" ) == 0 )
{
ReadTileset ( xNode );
}
if (strcmp(xNode->name(), "tileset") == 0)
ReadTileset(xNode);
else
if ( strcmp ( xNode->name (), "objectgroup" ) == 0 )
{
ReadObjects ( xNode );
}
if (strcmp(xNode->name(), "objectgroup") == 0)
ReadObjects(xNode);
}
// Generate global id table.
for ( auto pTileset : m_tileset )
for (auto tileset : mTilesets)
mGidTable.push_back(tileset->GetFirstGid());
std::sort(mGidTable.rbegin(), mGidTable.rend());
}
const TmxLayer* TmxReader::GetLayer(const std::string& aName) const
{
for (auto layer : mLayers)
{
m_gidTable.push_back ( pTileset->GetFirstGid () );
if (layer->GetName() == aName)
return layer;
}
std::sort ( m_gidTable.rbegin (), m_gidTable.rend () );
}
int CTmxReader::GetWidth () const
{
return m_width;
}
int CTmxReader::GetHeight () const
{
return m_height;
}
const CTmxLayer* CTmxReader::GetLayer ( int a_id ) const
{
return m_layers[a_id];
}
const CTmxLayer* CTmxReader::GetLayer ( std::string a_strName ) const
{
for ( auto pLay : m_layers )
{
if ( pLay->GetName ().compare ( a_strName ) == 0 )
{
return pLay;
}
}
return nullptr;
}
int CTmxReader::GetLayerCount () const
uint32_t TmxReader::LidFromGid(uint32_t aGid)
{
return m_layers.size ();
}
const CTmxObject* CTmxReader::GetObject ( int a_id ) const
{
return m_objects[a_id];
}
int CTmxReader::GetObjectCount () const
{
return m_objects.size ();
}
uint32_t CTmxReader::LidFromGid ( uint32_t a_uiGid )
{
for ( uint32_t uiFirst : m_gidTable )
for (uint32_t first : mGidTable)
{
if ( uiFirst <= a_uiGid )
{
return a_uiGid - ( uiFirst - 1 );
}
if (first <= aGid)
return aGid - (first - 1);
}
return a_uiGid;
return aGid;
}

View File

@@ -1,48 +0,0 @@
#ifndef __TMXREADER_H__
#define __TMXREADER_H__
#include <istream>
#include <vector>
#include <string>
#include <cstdint>
#include <rapidxml/rapidxml.hpp>
class CTmxTileset;
class CTmxLayer;
class CTmxObject;
class CTmxReader
{
public:
CTmxReader ();
~CTmxReader ();
void Open ( std::istream& a_in );
int GetWidth () const;
int GetHeight () const;
const CTmxLayer* GetLayer ( int a_id ) const;
const CTmxLayer* GetLayer ( std::string a_strName ) const;
int GetLayerCount () const;
const CTmxObject* GetObject ( int a_id ) const;
int GetObjectCount () const;
uint32_t LidFromGid ( uint32_t a_gid );
private:
bool DecodeMap ( uint32_t* a_out, size_t a_outSize, const std::string& a_strIn );
void ReadTileset ( rapidxml::xml_node<>* a_xNode );
void ReadLayer ( rapidxml::xml_node<>* a_xNode );
void ReadObjects ( rapidxml::xml_node<>* a_xNode );
int m_width, m_height;
std::vector<CTmxTileset*> m_tileset;
std::vector<CTmxLayer*> m_layers;
std::vector<CTmxObject*> m_objects;
std::vector<uint32_t> m_gidTable;
};
#endif//__TMXREADER_H__

48
src/tmxreader.hpp Normal file
View File

@@ -0,0 +1,48 @@
#ifndef TMXREADER_H
#define TMXREADER_H
#include <istream>
#include <vector>
#include <string>
#include <cstdint>
#include <rapidxml/rapidxml.hpp>
class TmxTileset;
class TmxLayer;
class TmxObject;
class TmxReader
{
public:
TmxReader() = default;
~TmxReader();
void Open(std::istream& aIn);
constexpr int GetWidth() const { return mWidth; }
constexpr int GetHeight() const { return mHeight; }
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(); }
inline const TmxObject* GetObject(size_t aId) const { return mObjects.at(aId); }
inline size_t GetObjectCount() const { return mObjects.size(); }
uint32_t LidFromGid(uint32_t aGid);
private:
static bool DecodeMap(uint32_t* aOut, size_t aOutSize, const std::string& aStrIn);
void ReadTileset(rapidxml::xml_node<>* aXNode);
void ReadLayer(rapidxml::xml_node<>* aXNode);
void ReadObjects(rapidxml::xml_node<>* aXNode);
int mWidth, mHeight;
std::vector<TmxTileset*> mTilesets;
std::vector<TmxLayer*> mLayers;
std::vector<TmxObject*> mObjects;
std::vector<uint32_t> mGidTable;
};
#endif//TMXREADER_H

View File

@@ -1,25 +0,0 @@
#ifndef __TMXTILESET_H__
#define __TMXTILESET_H__
#include <string>
#include <cstdint>
class CTmxTileset
{
public:
CTmxTileset ();
CTmxTileset ( const char* a_szName, const char* a_szSource, uint32_t a_uiFirstGid );
~CTmxTileset ();
const std::string& GetName () const;
const std::string& GetSource () const;
uint32_t GetFirstGid () const;
private:
std::string m_strName;
std::string m_strSource;
uint32_t m_uiFirstGid;
};
#endif//__TMXTILESET_H__

View File

@@ -1,6 +1,6 @@
/* tmxtileset.cpp
Copyright (C) 2015 a dinosaur
Copyright (C) 2015-2022 a dinosaur
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
@@ -20,41 +20,30 @@
*/
#include "tmxtileset.h"
#ifndef TMXTILESET_H
#define TMXTILESET_H
CTmxTileset::CTmxTileset () :
m_strName ( "" ),
m_strSource ( "" ),
m_uiFirstGid ( 0 )
#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; }
CTmxTileset::CTmxTileset ( const char* a_szName, const char* a_szSource, uint32_t a_uiFirstGid ) :
m_strName ( a_szName ),
m_strSource ( a_szSource ),
m_uiFirstGid ( a_uiFirstGid )
{
private:
std::string mName;
std::string mSource;
uint32_t mFirstGid;
}
};
CTmxTileset::~CTmxTileset ()
{
}
const std::string& CTmxTileset::GetName () const
{
return m_strName;
}
const std::string& CTmxTileset::GetSource () const
{
return m_strSource;
}
uint32_t CTmxTileset::GetFirstGid () const
{
return m_uiFirstGid;
}
#endif//TMXTILESET_H