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 add_executable(tmx2gba
tmx2gba.cpp tmx2gba.cpp
tmxlayer.cpp tmxlayer.h tmxlayer.hpp
tmxobject.cpp tmxobject.h tmxobject.hpp
tmxreader.cpp tmxreader.h tmxreader.hpp tmxreader.cpp
tmxtileset.cpp tmxtileset.h) tmxtileset.hpp)
target_link_libraries(tmx2gba target_link_libraries(tmx2gba
External::base64 External::base64
External::miniz External::miniz

View File

@@ -20,9 +20,9 @@
*/ */
#include "tmxreader.h" #include "tmxreader.hpp"
#include "tmxlayer.h" #include "tmxlayer.hpp"
#include "tmxobject.h" #include "tmxobject.hpp"
#include <iostream> #include <iostream>
#include <iomanip> #include <iomanip>
#include <fstream> #include <fstream>
@@ -34,22 +34,9 @@
#include <ultragetopt.h> #include <ultragetopt.h>
using std::cout; const std::string helpUsage = "Usage: tmx2gba [-h] [-f file] [-r offset] [-lyc name] [-p 0-15] [-m name;id] <-i inpath> <-o outpath>";
using std::cerr; const std::string helpShort = "Run 'tmx2gba -h' to view all available options.";
using std::endl; const std::string helpFull = R"(
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"(
-h ------------ Display this help & command info. -h ------------ Display this help & command info.
-l <name> ----- Name of layer to use (default first layer in TMX). -l <name> ----- Name of layer to use (default first layer in TMX).
-y <name> ----- Layer for palette mappings. -y <name> ----- Layer for palette mappings.
@@ -61,66 +48,44 @@ static const string g_strHelpFull = R"(
-o <path> ----- Path to output files. -o <path> ----- Path to output files.
-f <file> ----- Specify a file to use for flags, will override any options specified on the command line.)"; -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; bool help = false;
string inPath, outPath; std::string inPath, outPath;
string layer, collisionlay, paletteLay; std::string layer, collisionlay, paletteLay;
string flagFile; std::string flagFile;
int offset = 0; int offset = 0;
int palette = 0; int palette = 0;
vector<string> objMappings; std::vector<std::string> objMappings;
bool objExport = false; 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; 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' ): case ('h'):
params->help = true; p.help = true;
return; return;
case ( 'l' ): case ('l'): p.layer = optarg; break;
params->layer = optarg; 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; break;
case ( 'c' ): case ('i'): p.inPath = optarg; break;
params->collisionlay = optarg; case ('o'): p.outPath = optarg; break;
break; case ('f'): p.flagFile = 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;
default: default:
break; 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. // Check my paranoia.
if ( params->inPath.empty () ) if (params.inPath.empty())
{ {
cerr << "No input file specified." << endl; std::cerr << "No input file specified." << std::endl;
cout << g_strUsage << endl << g_strHelpShort << endl; std::cout << helpUsage << std::endl << helpShort << std::endl;
return false; return false;
} }
if ( params->outPath.empty () ) if (params.outPath.empty())
{ {
cerr << "No output file specified." << endl; std::cerr << "No output file specified." << std::endl;
cout << g_strUsage << endl << g_strHelpShort << endl; std::cout << helpUsage << std::endl << helpShort << std::endl;
return false; return false;
} }
if ( params->palette < 0 || params->palette > 15 ) if (params.palette < 0 || params.palette > 15)
{ {
cerr << "Invalid palette index." << endl; std::cerr << "Invalid palette index." << std::endl;
cout << g_strUsage << endl << g_strHelpShort << endl; std::cout << helpUsage << std::endl << helpShort << std::endl;
return false; 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> 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; int col = 0;
string datType = "ERR"; aOut.setf(std::ios::hex, std::ios::basefield);
if ( sizeof(T) == 1 ) aOut.setf(std::ios::showbase);
{
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 );
size_t i = 0; size_t i = 0;
for ( T element : a_dat ) for (T element : aDat)
{ {
if ( col == 0 ) if (col == 0)
{ aOut << "\t" << DatType<T>() << " ";
a_fout << "\t" << datType << " ";
}
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 else
{ {
a_fout << "" << endl; aOut << "" << std::endl;
col = 0; 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; Arguments p;
ParseArgs ( argc, argv, &params ); 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; return 0;
} }
if ( params.flagFile.length () != 0 ) if (!p.flagFile.empty())
{ {
ifstream paramFile ( params.flagFile ); std::ifstream paramFile(p.flagFile);
if ( !paramFile.is_open () ) if (!paramFile.is_open())
{ {
cerr << "Failed to open param file." << endl; std::cerr << "Failed to open param file." << std::endl;
return -1; return -1;
} }
vector<string> fileArgTokens; std::vector<std::string> fileArgTokens;
fileArgTokens.push_back ( "auu~~" ); fileArgTokens.push_back("auu~~");
bool carry = false; bool carry = false;
string token; std::string rawToken;
while ( !paramFile.eof () ) while (!paramFile.eof())
{ {
if ( carry ) if (carry)
{ {
string tmp; std::string tmp;
paramFile >> tmp; paramFile >> tmp;
token += " "; rawToken += " ";
token += tmp; rawToken += tmp;
} }
else else
{ {
token.clear (); rawToken.clear();
paramFile >> token; paramFile >> rawToken;
} }
if ( token == "" ) if (rawToken.empty())
{
continue; continue;
}
bool qFr = token[0] == '"'; bool qFr = rawToken[0] == '"';
bool qBk = token[token.length () - 1] == '"'; bool qBk = rawToken[rawToken.length() - 1] == '"';
if ( qFr && qBk ) if (qFr && qBk)
{ {
fileArgTokens.push_back ( token.substr ( 1, token.length () - 2 ) ); fileArgTokens.push_back(rawToken.substr(1, rawToken.length() - 2));
} }
else 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; carry = true;
} }
else 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; carry = false;
} }
else else
{ {
fileArgTokens.push_back ( token ); fileArgTokens.push_back(rawToken);
} }
} }
vector<const char*> fileArgs; std::vector<const char*> fileArgs;
fileArgs.reserve ( fileArgTokens.size () ); fileArgs.reserve(fileArgTokens.size());
for ( auto& token : fileArgTokens ) for (const auto& token : fileArgTokens)
{ fileArgs.push_back(token.c_str());
fileArgs.push_back ( token.c_str () );
}
fileArgs.push_back(nullptr); 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; return -1;
}
// Object mappings. // Object mappings.
map<string, uint32_t> objMapping; std::map<std::string, uint32_t> objMapping;
if ( params.objExport ) if (p.objExport)
{ {
for ( auto token : params.objMappings ) for (const auto& objToken : p.objMappings)
{ {
int splitter = token.find_last_of ( ';' ); auto splitter = objToken.find_last_of(';');
if ( splitter == -1 ) if (splitter == std::string::npos)
{ {
cerr << "Malformed mapping (missing a splitter)." << endl; std::cerr << "Malformed mapping (missing a splitter)." << std::endl;
return -1; return -1;
} }
try try
{ {
string name = token.substr ( 0, splitter ); std::string name = objToken.substr(0, splitter);
int id = stoi ( token.substr ( splitter + 1 ) ); int id = std::stoi(objToken.substr(splitter + 1));
objMapping[name] = id; 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. // Open & read input file.
CTmxReader tmx; TmxReader tmx;
ifstream fin ( params.inPath ); std::ifstream fin(p.inPath);
if ( !fin.is_open () ) if (!fin.is_open())
{ {
cerr << "Failed to open input file." << endl; std::cerr << "Failed to open input file." << std::endl;
return -1; return -1;
} }
tmx.Open ( fin ); tmx.Open(fin);
// Get layers. // Get layers.
if ( tmx.GetLayerCount () == 0 ) if (tmx.GetLayerCount() == 0)
{ {
cerr << "No layers found." << endl; std::cerr << "No layers found." << std::endl;
return -1; return -1;
} }
const CTmxLayer* pLayerGfx = params.layer.empty () ? tmx.GetLayer ( 0 ) : tmx.GetLayer ( params.layer ); const TmxLayer* layerGfx = p.layer.empty()
const CTmxLayer* pLayerCls = params.collisionlay.empty () ? nullptr : tmx.GetLayer ( params.collisionlay ); ? tmx.GetLayer(0)
const CTmxLayer* pLayerPal = params.paletteLay.empty () ? nullptr : tmx.GetLayer ( params.paletteLay ); : 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; return -1;
} }
// Open output files. // Open output files.
ofstream foutS ( params.outPath + ".s" ); std::ofstream foutS(p.outPath + ".s");
ofstream foutH ( params.outPath + ".h" ); std::ofstream foutH(p.outPath + ".h");
if ( !foutS.is_open () || !foutH.is_open () ) if (!foutS.is_open() || !foutH.is_open())
{ {
cerr << "Failed to create output file(s)."; std::cerr << "Failed to create output file(s).";
return -1; return -1;
} }
int slashPos = max ( (int)params.outPath.find_last_of ( '/' ), (int)params.outPath.find_last_of ( '\\' ) ); int slashPos = std::max((int)p.outPath.find_last_of('/'), (int)p.outPath.find_last_of('\\'));
string name = params.outPath; std::string name = p.outPath;
if ( slashPos != -1 ) if (slashPos != -1)
{ name = name.substr(slashPos + 1);
name = name.substr ( slashPos + 1 );
}
// Write header guards. // Write header guards.
string guard = "__TMX2GBA_" + name + "__"; std::string guard = "TMX2GBA_" + name;
for ( auto& c: guard ) c = (char)toupper ( (int)c ); for (auto& c: guard)
foutH << "#ifndef " << guard << endl; c = static_cast<char>(toupper(c));
foutH << "#define " << guard << endl; foutH << "#ifndef " << guard << std::endl;
foutH << endl; foutH << "#define " << guard << std::endl;
foutH << "#define " << name << "Width " << tmx.GetWidth () << endl; foutH << std::endl;
foutH << "#define " << name << "Height " << tmx.GetHeight () << endl; foutH << "#define " << name << "Width " << tmx.GetWidth() << std::endl;
foutH << endl; foutH << "#define " << name << "Height " << tmx.GetHeight() << std::endl;
foutH << std::endl;
// Convert to GBA-friendly charmap data. // Convert to GBA-friendly charmap data.
const uint32_t* pRead = pLayerGfx->GetData (); const uint32_t* gfxTiles = layerGfx->GetData();
const uint32_t* pPalRead = pLayerPal == nullptr ? nullptr : pLayerPal->GetData (); const uint32_t* palTiles = (layerPal == nullptr) ? nullptr : layerPal->GetData();
vector<uint16_t> vucCharDat; std::vector<uint16_t> charDat;
vucCharDat.reserve ( pLayerGfx->GetWidth () * pLayerGfx->GetHeight () ); size_t numTiles = static_cast<size_t>(layerGfx->GetWidth()) * static_cast<size_t>(layerGfx->GetHeight());
for ( size_t i = 0; i < size_t(pLayerGfx->GetWidth () * pLayerGfx->GetHeight ()); ++i ) 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 ); uint16_t tile = (uint16_t)std::max<int32_t>(0, tmx.LidFromGid(read & ~TmxLayer::FLIP_MASK) + p.offset);
uint8_t ucFlags = 0x0; uint8_t flags = 0x0;
// Get flipped! // Get flipped!
ucFlags |= ( uiRead & FLIP_HORZ ) ? 0x4 : 0x0; flags |= (read & TmxLayer::FLIP_HORZ) ? 0x4 : 0x0;
ucFlags |= ( uiRead & FLIP_VERT ) ? 0x8 : 0x0; flags |= (read & TmxLayer::FLIP_VERT) ? 0x8 : 0x0;
// Determine palette ID. // Determine palette ID.
uint32_t uiIndex = 0; uint32_t idx = 0;
if ( pPalRead != nullptr ) if (palTiles != nullptr)
{ idx = tmx.LidFromGid((*palTiles++) & ~TmxLayer::FLIP_MASK);
uiIndex = tmx.LidFromGid ( (*pPalRead++) & ~FLIP_MASK ); if (idx == 0)
} idx = p.palette + 1;
if ( uiIndex == 0 ) flags |= static_cast<uint8_t>(idx - 1) << 4;
{
uiIndex = params.palette + 1;
}
ucFlags |= (uint8_t)(uiIndex - 1) << 4;
vucCharDat.push_back ( usTile | ( uint16_t(ucFlags) << 8 ) ); charDat.push_back(tile | (static_cast<uint16_t>(flags) << 8));
} }
// Save out charmap. // Save out charmap.
foutH << "#define " << name << "TilesLen " << vucCharDat.size () * 2 << endl; foutH << "#define " << name << "TilesLen " << charDat.size() * 2 << std::endl;
foutH << "extern const unsigned short " << name << "Tiles[" << vucCharDat.size () << "];" << endl; foutH << "extern const unsigned short " << name << "Tiles[" << charDat.size() << "];" << std::endl;
foutH << endl; foutH << std::endl;
foutS << "\t.section .rodata" << endl; foutS << "\t.section .rodata" << std::endl;
foutS << "\t.align 2" << endl; foutS << "\t.align 2" << std::endl;
foutS << "\t.global " << name << "Tiles" << endl; foutS << "\t.global " << name << "Tiles" << std::endl;
foutS << "\t.hidden " << name << "Tiles" << endl; foutS << "\t.hidden " << name << "Tiles" << std::endl;
foutS << name << "Tiles" << ":" << endl; foutS << name << "Tiles" << ":" << std::endl;
WriteArray<uint16_t> ( foutS, vucCharDat ); WriteArray<uint16_t>(foutS, charDat);
foutS << endl; foutS << std::endl;
// Convert collision map & save it out. // Convert collision map & save it out.
if ( pLayerCls != nullptr ) if (layerCls != nullptr)
{ {
vector<uint8_t> vucCollisionDat; std::vector<uint8_t> vucCollisionDat;
vucCollisionDat.reserve ( pLayerCls->GetWidth () * pLayerCls->GetHeight () ); vucCollisionDat.reserve(layerCls->GetWidth() * layerCls->GetHeight());
pRead = pLayerCls->GetData (); gfxTiles = layerCls->GetData();
for ( int i = 0; i < pLayerCls->GetWidth () * pLayerCls->GetHeight (); ++i ) for (int i = 0; i < layerCls->GetWidth() * layerCls->GetHeight(); ++i)
{ {
uint8_t ucTile = (uint8_t)tmx.LidFromGid ( (*pRead++) & ~FLIP_MASK ); uint8_t ucTile = (uint8_t)tmx.LidFromGid((*gfxTiles++) & ~TmxLayer::FLIP_MASK);
vucCollisionDat.push_back ( ucTile ); vucCollisionDat.push_back(ucTile);
} }
// Try to nicely append "_collision" to the output name. // Try to nicely append "_collision" to the output name.
string strPath; std::string path;
size_t extPos = params.outPath.find_last_of ( '.' ); size_t extPos = p.outPath.find_last_of('.');
if ( extPos != string::npos ) if (extPos != std::string::npos)
{ path = p.outPath.insert(extPos, "_collision");
strPath = params.outPath.insert ( extPos, "_collision" );
}
else else
{ path = p.outPath + "_collision";
strPath = params.outPath + "_collision";
}
// Save it out. // Save it out.
foutH << "#define " << name << "CollisionLen " << vucCollisionDat.size () << endl; foutH << "#define " << name << "CollisionLen " << vucCollisionDat.size() << std::endl;
foutH << "extern const unsigned char " << name << "Collision[" << vucCollisionDat.size () << "];" << endl; foutH << "extern const unsigned char " << name << "Collision[" << vucCollisionDat.size() << "];" << std::endl;
foutH << endl; foutH << std::endl;
foutS << endl; foutS << std::endl;
foutS << "\t.section .rodata" << endl; foutS << "\t.section .rodata" << std::endl;
foutS << "\t.align 2" << endl; foutS << "\t.align 2" << std::endl;
foutS << "\t.global " << name << "Collision" << endl; foutS << "\t.global " << name << "Collision" << std::endl;
foutS << "\t.hidden " << name << "Collision" << endl; foutS << "\t.hidden " << name << "Collision" << std::endl;
foutS << name << "Collision" << ":" << endl; foutS << name << "Collision" << ":" << std::endl;
WriteArray<uint8_t> ( foutS, vucCollisionDat ); WriteArray<uint8_t>(foutS, vucCollisionDat);
foutS << endl; foutS << std::endl;
} }
if ( params.objExport ) if (p.objExport)
{ {
vector<uint32_t> objDat; std::vector<uint32_t> objDat;
for ( int i = 0; i < tmx.GetObjectCount (); ++i ) 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; continue;
}
float x, y; float x, y;
obj->GetPos ( &x, &y ); obj->GetPos(x, y);
objDat.push_back ( it->second ); objDat.push_back(it->second);
objDat.push_back ( (int)( x * 256.0f ) ); objDat.push_back((int)(x * 256.0f));
objDat.push_back ( (int)( y * 256.0f ) ); objDat.push_back((int)(y * 256.0f));
} }
// Save it out. // Save it out.
foutH << "#define " << name << "ObjCount " << objDat.size () / 3 << endl; foutH << "#define " << name << "ObjCount " << objDat.size() / 3 << std::endl;
foutH << "#define " << name << "ObjdatLen " << objDat.size () * sizeof(int) << endl; foutH << "#define " << name << "ObjdatLen " << objDat.size() * sizeof(int) << std::endl;
foutH << "extern const unsigned int " << name << "Objdat[" << objDat.size () << "];" << endl; foutH << "extern const unsigned int " << name << "Objdat[" << objDat.size() << "];" << std::endl;
foutH << endl; foutH << std::endl;
foutS << endl; foutS << std::endl;
foutS << "\t.section .rodata" << endl; foutS << "\t.section .rodata" << std::endl;
foutS << "\t.align 2" << endl; foutS << "\t.align 2" << std::endl;
foutS << "\t.global " << name << "Objdat" << endl; foutS << "\t.global " << name << "Objdat" << std::endl;
foutS << "\t.hidden " << name << "Objdat" << endl; foutS << "\t.hidden " << name << "Objdat" << std::endl;
foutS << name << "Objdat" << ":" << endl; foutS << name << "Objdat" << ":" << std::endl;
WriteArray<uint32_t> ( foutS, objDat ); WriteArray<uint32_t>(foutS, objDat);
foutS << endl; foutS << std::endl;
} }
foutH << "#endif//" << guard << endl; foutH << "#endif//" << guard << std::endl;
foutH.close (); foutH.close();
foutS.close (); foutS.close();
return 0; 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 /* tmxobject.cpp
Copyright (C) 2015 a dinosaur Copyright (C) 2015-2022 a dinosaur
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 () : #include <string>
m_name ( "" ), #include <utility>
m_x ( 0.0f ), m_y ( 0.0f )
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 ) : private:
m_name ( a_name ), std::string mName;
m_x ( a_x ), m_y ( a_y ) float mX, mY;
{ };
}
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;
}
}
#endif//TMXOBJECT_H

View File

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

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 /* tmxtileset.cpp
Copyright (C) 2015 a dinosaur Copyright (C) 2015-2022 a dinosaur
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages 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 () : #include <string>
m_strName ( "" ), #include <cstdint>
m_strSource ( "" ), #include <utility>
m_uiFirstGid ( 0 )
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 ) : private:
m_strName ( a_szName ), std::string mName;
m_strSource ( a_szSource ), std::string mSource;
m_uiFirstGid ( a_uiFirstGid ) uint32_t mFirstGid;
{
} };
CTmxTileset::~CTmxTileset () #endif//TMXTILESET_H
{
}
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;
}