From b7e320824b3a396a6fc527eba792a399504f9092 Mon Sep 17 00:00:00 2001 From: ScrelliCopter Date: Mon, 26 Oct 2015 17:09:27 +1100 Subject: [PATCH] Tiles are now local IDs (relative to the tileset), making collision data usable. Palettes can be overridden per-tile thru a layer. --- .gitignore | 2 + palette.png | Bin 0 -> 823 bytes src/tmx2gba.cpp | 41 ++++++---- src/tmxlayer.cpp | 4 +- src/tmxlayer.h | 11 ++- src/tmxreader.cpp | 171 +++++++++++++++++++++++++++++----------- src/tmxreader.h | 14 +++- src/tmxtileset.cpp | 60 ++++++++++++++ src/tmxtileset.h | 25 ++++++ tmx2gba.vcxproj | 2 + tmx2gba.vcxproj.filters | 6 ++ 11 files changed, 269 insertions(+), 67 deletions(-) create mode 100644 palette.png create mode 100644 src/tmxtileset.cpp create mode 100644 src/tmxtileset.h diff --git a/.gitignore b/.gitignore index 00c70e0..472687b 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,5 @@ Release/ # Some files I used for testing. *.tmx *.raw +plains.png +editor.png diff --git a/palette.png b/palette.png new file mode 100644 index 0000000000000000000000000000000000000000..b83ba76c2b8469d3e378112abec0111c6df4cdcb GIT binary patch literal 823 zcmV-71IYY|P)nu~ zK~z}7#aF*;6hRdJ?zq+>SOiQFlKN^vjJfV0Sl%Dt10nT6U-6q3R!lFPHWdEVR2?(Ch3$!qq_eKYgk``(Y;YcP-^D(i6=6h)8l zD2lfL0LF}0&W&%g)&l^NWc<77PENQU!R`ip5by^9TMXw<0{#O4aNhv)ZsO^wg$ID2 zi8vs}#jw`3xL$R*Zjt^P)_TDxFm^`PP0S(^c`0_{>yFPmkTyQM1Jg)J^qPfJ3$N4B zye;F}=+z>W!YdYGRSoDu)W*vSQAICK=oB>WybWK1qZ{7VnE&#kX2WyT&w+qZ( zkXd*!nnkF-X=%+uf2i)k0Ak2{^sWHf(@D)eNGKH5Oh+(GU0?#hU#g;d`*@2%{vQ$3yk4*mi_(1-#=3nf2(n>TcQ1 ze{bd;xU}_!(+-y(+JEbYUN|2dDSsPkCW<14%NP(A~S4Zf)PU+|yX@DHC8saW;9HhBO5002ovPDHLkV1j^O BeE|Rf literal 0 HcmV?d00001 diff --git a/src/tmx2gba.cpp b/src/tmx2gba.cpp index 487f9f3..d741f59 100644 --- a/src/tmx2gba.cpp +++ b/src/tmx2gba.cpp @@ -31,11 +31,12 @@ #include -static const std::string g_strUsage = "Usage: tmx2gba [-h] [-r offset] [-lc name] [-p 0-15] <-i inpath> <-o outpath>\nRun 'tmx2gba -h' to view all available options."; -static const std::string g_strFullHelp = R"(Usage: tmx2gba [-hr] [-p index] <-i inpath> <-o outpath> +static const std::string g_strUsage = "Usage: tmx2gba [-h] [-r offset] [-lyc name] [-p 0-15] <-i inpath> <-o outpath>\nRun 'tmx2gba -h' to view all available options."; +static const std::string g_strFullHelp = R"(Usage: tmx2gba [-h] [-r offset] [-lyc name] [-p 0-15] <-i inpath> <-o outpath> -h ---------- Display this help & command info. -l --- Name of layer to use (default first layer in TMX). +-y --- Layer for palette mappings. -c --- Output a separate 8bit collision map of the specified layer. -r - Offset tile indices (default 0). -p <0-15> --- Select which palette to use for 4-bit tilesets. @@ -154,23 +155,34 @@ int main ( int argc, char** argv ) } // Convert to GBA-friendly charmap data. - const uint16_t* pRead = (const uint16_t*)pLayerGfx->GetData (); - //const uint16_t* pPalRead = (const uint16_t*)pLayerPal->GetData (); + const uint32_t* pRead = pLayerGfx->GetData (); + const uint32_t* pPalRead = pLayerPal == nullptr ? nullptr : pLayerPal->GetData (); std::vector vucCharDat; vucCharDat.reserve ( pLayerGfx->GetWidth () * pLayerGfx->GetHeight () ); for ( size_t i = 0; i < size_t(pLayerGfx->GetWidth () * pLayerGfx->GetHeight () * 2); ++i ) { - uint16_t usTile = std::max ( (*pRead++) + (uint16_t)params.offset, 0 ); + uint32_t uiRead = (*pRead++); - bool bFlipH = ( 0x8000 & *pRead ) ? true : false; - bool bFlipV = ( 0x4000 & *pRead++ ) ? true : false; + uint8_t ucTile = (uint8_t)std::max ( 0, tmx.LidFromGid ( uiRead & ~FLIP_MASK ) + params.offset ); + uint8_t ucFlags = 0x0; - uint8_t ucFlags = 0x0; - ucFlags |= ( bFlipH ) ? 0x4 : 0x0; - ucFlags |= ( bFlipV ) ? 0x8 : 0x0; - ucFlags |= params.palette << 4; + // Get flipped! + ucFlags |= ( uiRead & FLIP_HORZ ) ? 0x4 : 0x0; + ucFlags |= ( uiRead & FLIP_VERT ) ? 0x8 : 0x0; - vucCharDat.push_back ( usTile | ucFlags << 8 ); + // Determine palette ID. + uint32_t uiIndex = 0; + if ( pPalRead != nullptr ) + { + uiIndex = tmx.LidFromGid ( *pPalRead++ & ~FLIP_MASK ); + } + if ( uiIndex == 0 ) + { + uiIndex = ( params.palette << 4 ) + 1; + } + ucFlags |= (uint8_t)(uiIndex - 1); + + vucCharDat.push_back ( (uint16_t)ucTile | (uint16_t)ucFlags << 8 ); } // Save out charmap. @@ -189,11 +201,10 @@ int main ( int argc, char** argv ) std::vector vucCollisionDat; vucCollisionDat.reserve ( pLayerCls->GetWidth () * pLayerCls->GetHeight () ); - const uint8_t* pRead = (const uint8_t*)pLayerCls->GetData (); + const uint32_t* pRead = pLayerCls->GetData (); for ( size_t i = 0; i < pLayerCls->GetWidth () * pLayerCls->GetHeight (); ++i ) { - uint8_t ucTile = *pRead; - pRead += 4; + uint8_t ucTile = (uint8_t)tmx.LidFromGid ( (*pRead++) & ~FLIP_MASK ); vucCollisionDat.push_back ( ucTile ); } diff --git a/src/tmxlayer.cpp b/src/tmxlayer.cpp index 3db2fa6..3d6c523 100644 --- a/src/tmxlayer.cpp +++ b/src/tmxlayer.cpp @@ -32,7 +32,7 @@ CTmxLayer::CTmxLayer () : } -CTmxLayer::CTmxLayer ( int a_iWidth, int a_iHeight, const char* a_szName, uint8_t* a_pTileDat ) : +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 ), @@ -62,7 +62,7 @@ int CTmxLayer::GetHeight () const return m_iHeight; } -const uint8_t* CTmxLayer::GetData () const +const uint32_t* CTmxLayer::GetData () const { return m_pTileDat; } diff --git a/src/tmxlayer.h b/src/tmxlayer.h index 79834cd..5949122 100644 --- a/src/tmxlayer.h +++ b/src/tmxlayer.h @@ -4,22 +4,27 @@ #include #include +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, uint8_t* a_pTileDat ); + 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 uint8_t* GetData () const; + const uint32_t* GetData () const; private: std::string m_strName; int m_iWidth, m_iHeight; - uint8_t* m_pTileDat; + uint32_t* m_pTileDat; }; diff --git a/src/tmxreader.cpp b/src/tmxreader.cpp index e41dc3f..582654a 100644 --- a/src/tmxreader.cpp +++ b/src/tmxreader.cpp @@ -21,9 +21,11 @@ */ #include "tmxreader.h" +#include "tmxtileset.h" #include "tmxlayer.h" #include #include +#include #include #include #include @@ -36,6 +38,13 @@ CTmxReader::CTmxReader () CTmxReader::~CTmxReader () { + // Delete old tilesets. + for ( auto pTileset : m_vpTileset ) + { + delete pTileset; + } + m_vpTileset.clear (); + // Delete old layers. for ( auto pLay : m_vpLayers ) { @@ -45,7 +54,7 @@ CTmxReader::~CTmxReader () } -bool DecodeMap ( uint8_t* a_szOut, size_t a_outSize, const std::string& a_strIn ) +bool CTmxReader::DecodeMap ( uint32_t* a_pOut, size_t a_outSize, const std::string& a_strIn ) { // Decode base64 string. size_t cutTheCrap = a_strIn.find_first_not_of ( " \t\n\r" ); @@ -54,8 +63,8 @@ bool DecodeMap ( uint8_t* a_szOut, size_t a_outSize, const std::string& a_strIn // Decompress compressed data. mz_ulong uiDstSize = a_outSize; int iRes = uncompress ( - a_szOut, &uiDstSize, - (const uint8_t*)strDec.data (), strDec.size () + (unsigned char*)a_pOut, &uiDstSize, + (const unsigned char*)strDec.data (), strDec.size () ); strDec.clear (); if ( iRes < 0 ) @@ -66,8 +75,91 @@ bool DecodeMap ( uint8_t* a_szOut, size_t a_outSize, const std::string& a_strIn return true; } +void CTmxReader::ReadTileset ( rapidxml::xml_node<>* a_xNode ) +{ + rapidxml::xml_attribute<>* xAttrib; + + const char* szName = ""; + const char* szSource = ""; + uint32_t uiFirstGid = 0; + + // Read name. + xAttrib = a_xNode->first_attribute ( "name" ); + if ( xAttrib != nullptr ) + { + szName = xAttrib->value (); + } + + // Read source. + xAttrib = a_xNode->first_attribute ( "source" ); + if ( xAttrib != nullptr ) + { + szSource = xAttrib->value (); + } + + // Read first global ID. + xAttrib = a_xNode->first_attribute ( "firstgid" ); + if ( xAttrib != nullptr ) + { + uiFirstGid = std::stoul ( xAttrib->value () ); + } + + m_vpTileset.push_back ( new CTmxTileset ( szName, szSource, uiFirstGid ) ); +} + +void CTmxReader::ReadLayer ( rapidxml::xml_node<>* a_xNode ) +{ + rapidxml::xml_attribute<>* xAttrib; + const char* szName = ""; + int iWidth = 0; + int iHeight = 0; + uint32_t* pTileDat = nullptr; + + // Read name. + xAttrib = a_xNode->first_attribute ( "name" ); + if ( xAttrib != nullptr ) + { + szName = xAttrib->value (); + } + + // Read width. + xAttrib = a_xNode->first_attribute ( "width" ); + if ( xAttrib != nullptr ) + { + iWidth = std::stoi ( xAttrib->value () ); + } + + // Read height. + xAttrib = a_xNode->first_attribute ( "height" ); + if ( xAttrib != nullptr ) + { + iHeight = std::stoi ( xAttrib->value () ); + } + + // Read tile data. + auto xData = a_xNode->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 () ) ) ) + { + pTileDat = nullptr; + } + } + + m_vpLayers.push_back ( new CTmxLayer ( iWidth, iHeight, szName, pTileDat ) ); +} + void CTmxReader::Open ( std::istream& a_in ) { + // Delete old tilesets. + for ( auto pTileset : m_vpTileset ) + { + delete pTileset; + } + m_vpTileset.clear (); + // Delete old layers. for ( auto pLay : m_vpLayers ) { @@ -75,6 +167,8 @@ void CTmxReader::Open ( std::istream& a_in ) } m_vpLayers.clear (); + m_vuiGidTable.clear (); + // Read string into a buffer. std::stringstream buf; buf << a_in.rdbuf (); @@ -103,56 +197,27 @@ void CTmxReader::Open ( std::istream& a_in ) m_iHeight = std::stoi ( xAttrib->value () ); } - // Read layer nodes. + // Read nodes. for ( auto xNode = xMap->first_node (); xNode != nullptr; xNode = xNode->next_sibling () ) { - // Make sure it's a layer. - if ( strcmp ( xNode->name (), "layer" ) != 0 ) + // Read layer nodes. + if ( strcmp ( xNode->name (), "layer" ) == 0 ) { - continue; + ReadLayer ( xNode ); } - - rapidxml::xml_attribute<>* xAttrib; - const char* szName = nullptr; - int iWidth = 0; - int iHeight = 0; - uint8_t* pTileDat = nullptr; - - // Read name. - xAttrib = xNode->first_attribute ( "name" ); - if ( xAttrib != nullptr ) + else + if ( strcmp ( xNode->name (), "tileset" ) == 0 ) { - szName = xAttrib->value (); + ReadTileset ( xNode ); } - - // Read width. - xAttrib = xNode->first_attribute ( "width" ); - if ( xAttrib != nullptr ) - { - iWidth = std::stoi ( xAttrib->value () ); - } - - // Read height. - xAttrib = xNode->first_attribute ( "height" ); - if ( xAttrib != nullptr ) - { - iHeight = std::stoi ( xAttrib->value () ); - } - - // Read tile data. - auto xData = xNode->first_node ( "data" ); - if ( xData != nullptr ) - { - // TODO: don't assume base64 & zlib. - pTileDat = new uint8_t[iWidth * iHeight * 4]; - if ( !DecodeMap ( pTileDat, iWidth * iHeight * 4, std::string ( xData->value () ) ) ) - { - pTileDat = nullptr; - } - } - - m_vpLayers.push_back ( new CTmxLayer ( iWidth, iHeight, szName, pTileDat ) ); } + + // Generate global id table. + for ( auto pTileset : m_vpTileset ) + { + m_vuiGidTable.push_back ( pTileset->GetFirstGid () ); + } + std::sort ( m_vuiGidTable.rbegin (), m_vuiGidTable.rend () ); } @@ -189,3 +254,17 @@ int CTmxReader::GetLayerCount () const { return m_vpLayers.size (); } + + +uint32_t CTmxReader::LidFromGid ( uint32_t a_uiGid ) +{ + for ( uint32_t uiFirst : m_vuiGidTable ) + { + if ( uiFirst <= a_uiGid ) + { + return a_uiGid - ( uiFirst - 1 ); + } + } + + return a_uiGid; +} diff --git a/src/tmxreader.h b/src/tmxreader.h index 5c2105e..fdaa110 100644 --- a/src/tmxreader.h +++ b/src/tmxreader.h @@ -3,8 +3,12 @@ #include #include +#include +#include +class CTmxTileset; class CTmxLayer; +namespace rapidxml { template class xml_node; } class CTmxReader { @@ -21,9 +25,17 @@ public: const CTmxLayer* GetLayer ( std::string a_strName ) const; int GetLayerCount () const; + uint32_t LidFromGid ( uint32_t a_uiGid ); + private: + bool DecodeMap ( uint32_t* a_pOut, size_t a_outSize, const std::string& a_strIn ); + void ReadTileset ( rapidxml::xml_node<>* a_xNode ); + void ReadLayer ( rapidxml::xml_node<>* a_xNode ); + int m_iWidth, m_iHeight; - std::vector m_vpLayers; + std::vector m_vpTileset; + std::vector m_vpLayers; + std::vector m_vuiGidTable; }; diff --git a/src/tmxtileset.cpp b/src/tmxtileset.cpp new file mode 100644 index 0000000..bbee519 --- /dev/null +++ b/src/tmxtileset.cpp @@ -0,0 +1,60 @@ +/* tmxtileset.cpp + + Copyright (C) 2015 Nicholas Curtis + + 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 "tmxtileset.h" + +CTmxTileset::CTmxTileset () : + m_strName ( "" ), + m_strSource ( "" ), + m_uiFirstGid ( 0 ) +{ + +} + +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 ) +{ + +} + +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; +} diff --git a/src/tmxtileset.h b/src/tmxtileset.h new file mode 100644 index 0000000..67996c9 --- /dev/null +++ b/src/tmxtileset.h @@ -0,0 +1,25 @@ +#ifndef __TMXTILESET_H__ +#define __TMXTILESET_H__ + +#include +#include + +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__ diff --git a/tmx2gba.vcxproj b/tmx2gba.vcxproj index db378e5..c8e6c92 100644 --- a/tmx2gba.vcxproj +++ b/tmx2gba.vcxproj @@ -71,6 +71,7 @@ + @@ -82,6 +83,7 @@ + diff --git a/tmx2gba.vcxproj.filters b/tmx2gba.vcxproj.filters index c64c194..b2be5db 100644 --- a/tmx2gba.vcxproj.filters +++ b/tmx2gba.vcxproj.filters @@ -30,6 +30,9 @@ Source Files + + Source Files + @@ -59,5 +62,8 @@ Header Files + + Header Files + \ No newline at end of file