mirror of
https://github.com/ScrelliCopter/tmx2gba.git
synced 2025-02-21 03:29:25 +11:00
Compare commits
19 Commits
385f7b069f
...
tmx
| Author | SHA1 | Date | |
|---|---|---|---|
| 5937455000 | |||
| 224e1e53e9 | |||
| b8d7d43899 | |||
| 056612667b | |||
| e6053f9472 | |||
| 6b786d36fb | |||
| 708fd13d08 | |||
| 6a6d589817 | |||
| fcb9eceec3 | |||
| 2638bf2667 | |||
| b29c61774c | |||
| 835b80256f | |||
| 677d59f096 | |||
| b6308816ae | |||
| 0dd9074e27 | |||
| 5daf57ffe1 | |||
| 6c52897942 | |||
| e6bb098e15 | |||
| c2e9f5c974 |
2
.github/workflows/cmake.yml
vendored
2
.github/workflows/cmake.yml
vendored
@@ -54,4 +54,4 @@ jobs:
|
|||||||
- uses: actions/upload-artifact@v4
|
- uses: actions/upload-artifact@v4
|
||||||
with:
|
with:
|
||||||
name: ${{env.ARTIFACT_NAME}}-${{matrix.config.artifact}}
|
name: ${{env.ARTIFACT_NAME}}-${{matrix.config.artifact}}
|
||||||
path: build/src/${{env.ARTIFACT_NAME}}${{startsWith(matrix.config.os, 'windows') && '.exe' || ''}}
|
path: build/src/${{env.ARTIFACT_NAME}}${{startsWith(matrix.config.os, 'windows') && '.[ep][xd][eb]' || ''}}
|
||||||
|
|||||||
@@ -8,7 +8,6 @@ project(tmx2gba
|
|||||||
option(USE_ZLIB "Use zlib instead of bundled miniz" "${UNIX}")
|
option(USE_ZLIB "Use zlib instead of bundled miniz" "${UNIX}")
|
||||||
option(USE_BUNDLED_PUGIXML "Use bundled PUGIXML" ON)
|
option(USE_BUNDLED_PUGIXML "Use bundled PUGIXML" ON)
|
||||||
option(USE_BUNDLED_ZSTD "Use bundled libzstd" ON)
|
option(USE_BUNDLED_ZSTD "Use bundled libzstd" ON)
|
||||||
option(USE_BUNDLED_TMXLITE "Use bundled tmxlite" ON)
|
|
||||||
|
|
||||||
option(TMX2GBA_DKP_INSTALL "Install into DEVKITPRO prefix" OFF)
|
option(TMX2GBA_DKP_INSTALL "Install into DEVKITPRO prefix" OFF)
|
||||||
|
|
||||||
@@ -41,7 +40,6 @@ else()
|
|||||||
endif()
|
endif()
|
||||||
|
|
||||||
add_subdirectory(ext/base64)
|
add_subdirectory(ext/base64)
|
||||||
add_subdirectory(ext/rapidxml)
|
|
||||||
|
|
||||||
# Main tmx2gba sources
|
# Main tmx2gba sources
|
||||||
add_subdirectory(src)
|
add_subdirectory(src)
|
||||||
|
|||||||
@@ -10,13 +10,12 @@ tmx2gba is a simple command line utility that converts [Tiled](http://www.mapedi
|
|||||||
|
|
||||||
## Usage ##
|
## Usage ##
|
||||||
```
|
```
|
||||||
tmx2gba [-hv] [-r offset] [-lyc name] [-p 0-15] [-m name;id] <-i inpath> <-o outpath>
|
tmx2gba [-h] [-r offset] [-lyc name] [-p 0-15] [-m name;id] <-i inpath> <-o outpath>
|
||||||
```
|
```
|
||||||
|
|
||||||
| Command | Required | Notes |
|
| Command | Required | Notes |
|
||||||
|--------------|----------|------------------------------------------------------------------------------------|
|
|--------------|----------|------------------------------------------------------------------------------------|
|
||||||
| -h | N/A | Display help & command info |
|
| -h | N/A | Display program help & command info then quit |
|
||||||
| -v | No | Display version & quit |
|
|
||||||
| -l (name) | No | Name of layer to use (default first layer in TMX) |
|
| -l (name) | No | Name of layer to use (default first layer in TMX) |
|
||||||
| -y (name) | No | Layer for palette mappings |
|
| -y (name) | No | Layer for palette mappings |
|
||||||
| -c (name) | No | Output a separate 8bit collision map of the specified layer |
|
| -c (name) | No | Output a separate 8bit collision map of the specified layer |
|
||||||
@@ -60,7 +59,6 @@ sudo cmake --install build
|
|||||||
|
|
||||||
## License ##
|
## License ##
|
||||||
[tmx2gba](https://github.com/ScrelliCopter/tmx2gba) is licensed under the [Zlib license](COPYING.txt).
|
[tmx2gba](https://github.com/ScrelliCopter/tmx2gba) is licensed under the [Zlib license](COPYING.txt).
|
||||||
- A modified [tmxlite](https://github.com/fallahn/tmxlite) is licensed under the [Zlib license](ext/tmxlite/LICENSE).
|
|
||||||
- [pugixml](https://pugixml.org/) is licensed under the [MIT license](ext/pugixml/LICENSE.md).
|
- [pugixml](https://pugixml.org/) is licensed under the [MIT license](ext/pugixml/LICENSE.md).
|
||||||
- [René Nyffenegger's base64.cpp](https://github.com/ReneNyffenegger/cpp-base64) is licensed under the [Zlib license](ext/base64/LICENSE).
|
- [René Nyffenegger's base64.cpp](https://github.com/ReneNyffenegger/cpp-base64) is licensed under the [Zlib license](ext/base64/LICENSE).
|
||||||
- [miniz](https://github.com/richgel999/miniz) is licensed under the [MIT license](ext/miniz/LICENSE).
|
- [miniz](https://github.com/richgel999/miniz) is licensed under the [MIT license](ext/miniz/LICENSE).
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
add_library(base64
|
add_library(base64
|
||||||
base64.cpp base64.h)
|
base64.cpp base64.h)
|
||||||
add_library(base64::base64 ALIAS base64)
|
add_library(base64::base64 ALIAS base64)
|
||||||
set_target_properties(base64 PROPERTIES CXX_STANDARD 17)
|
set_target_properties(base64 PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON)
|
||||||
|
target_compile_options(base64 PUBLIC $<$<CXX_COMPILER_ID:MSVC>:/Zc:__cplusplus>)
|
||||||
target_include_directories(base64
|
target_include_directories(base64
|
||||||
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ add_library(pugixml STATIC
|
|||||||
src/pugixml.hpp
|
src/pugixml.hpp
|
||||||
src/pugixml.cpp)
|
src/pugixml.cpp)
|
||||||
add_library(pugixml::static ALIAS pugixml)
|
add_library(pugixml::static ALIAS pugixml)
|
||||||
|
add_library(pugixml::pugixml ALIAS pugixml)
|
||||||
|
|
||||||
set_target_properties(pugixml PROPERTIES
|
set_target_properties(pugixml PROPERTIES
|
||||||
CXX_STANDARD_REQUIRED ON
|
CXX_STANDARD_REQUIRED ON
|
||||||
|
|||||||
@@ -1,4 +0,0 @@
|
|||||||
add_library(rapidxml INTERFACE)
|
|
||||||
add_library(External::rapidxml ALIAS rapidxml)
|
|
||||||
target_include_directories(rapidxml
|
|
||||||
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
|
|
||||||
@@ -1,52 +0,0 @@
|
|||||||
Use of this software is granted under one of the following two licenses,
|
|
||||||
to be chosen freely by the user.
|
|
||||||
|
|
||||||
1. Boost Software License - Version 1.0 - August 17th, 2003
|
|
||||||
===============================================================================
|
|
||||||
|
|
||||||
Copyright (c) 2006, 2007 Marcin Kalicinski
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person or organization
|
|
||||||
obtaining a copy of the software and accompanying documentation covered by
|
|
||||||
this license (the "Software") to use, reproduce, display, distribute,
|
|
||||||
execute, and transmit the Software, and to prepare derivative works of the
|
|
||||||
Software, and to permit third-parties to whom the Software is furnished to
|
|
||||||
do so, all subject to the following:
|
|
||||||
|
|
||||||
The copyright notices in the Software and this entire statement, including
|
|
||||||
the above license grant, this restriction and the following disclaimer,
|
|
||||||
must be included in all copies of the Software, in whole or in part, and
|
|
||||||
all derivative works of the Software, unless such copies or derivative
|
|
||||||
works are solely in the form of machine-executable object code generated by
|
|
||||||
a source language processor.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT
|
|
||||||
SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE
|
|
||||||
FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE,
|
|
||||||
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
|
||||||
DEALINGS IN THE SOFTWARE.
|
|
||||||
|
|
||||||
2. The MIT License
|
|
||||||
===============================================================================
|
|
||||||
|
|
||||||
Copyright (c) 2006, 2007 Marcin Kalicinski
|
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
|
||||||
in the Software without restriction, including without limitation the rights
|
|
||||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
||||||
of the Software, and to permit persons to whom the Software is furnished to do so,
|
|
||||||
subject to the following conditions:
|
|
||||||
|
|
||||||
The above copyright notice and this permission notice shall be included in all
|
|
||||||
copies or substantial portions of the Software.
|
|
||||||
|
|
||||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
||||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
||||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
||||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
||||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
||||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
|
|
||||||
IN THE SOFTWARE.
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -1,174 +0,0 @@
|
|||||||
#ifndef RAPIDXML_ITERATORS_HPP_INCLUDED
|
|
||||||
#define RAPIDXML_ITERATORS_HPP_INCLUDED
|
|
||||||
|
|
||||||
// Copyright (C) 2006, 2009 Marcin Kalicinski
|
|
||||||
// Version 1.13
|
|
||||||
// Revision $DateTime: 2009/05/13 01:46:17 $
|
|
||||||
//! \file rapidxml_iterators.hpp This file contains rapidxml iterators
|
|
||||||
|
|
||||||
#include "rapidxml.hpp"
|
|
||||||
|
|
||||||
namespace rapidxml
|
|
||||||
{
|
|
||||||
|
|
||||||
//! Iterator of child nodes of xml_node
|
|
||||||
template<class Ch>
|
|
||||||
class node_iterator
|
|
||||||
{
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef typename xml_node<Ch> value_type;
|
|
||||||
typedef typename xml_node<Ch> &reference;
|
|
||||||
typedef typename xml_node<Ch> *pointer;
|
|
||||||
typedef std::ptrdiff_t difference_type;
|
|
||||||
typedef std::bidirectional_iterator_tag iterator_category;
|
|
||||||
|
|
||||||
node_iterator()
|
|
||||||
: m_node(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
node_iterator(xml_node<Ch> *node)
|
|
||||||
: m_node(node->first_node())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
reference operator *() const
|
|
||||||
{
|
|
||||||
assert(m_node);
|
|
||||||
return *m_node;
|
|
||||||
}
|
|
||||||
|
|
||||||
pointer operator->() const
|
|
||||||
{
|
|
||||||
assert(m_node);
|
|
||||||
return m_node;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_iterator& operator++()
|
|
||||||
{
|
|
||||||
assert(m_node);
|
|
||||||
m_node = m_node->next_sibling();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_iterator operator++(int)
|
|
||||||
{
|
|
||||||
node_iterator tmp = *this;
|
|
||||||
++this;
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_iterator& operator--()
|
|
||||||
{
|
|
||||||
assert(m_node && m_node->previous_sibling());
|
|
||||||
m_node = m_node->previous_sibling();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_iterator operator--(int)
|
|
||||||
{
|
|
||||||
node_iterator tmp = *this;
|
|
||||||
++this;
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator ==(const node_iterator<Ch> &rhs)
|
|
||||||
{
|
|
||||||
return m_node == rhs.m_node;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator !=(const node_iterator<Ch> &rhs)
|
|
||||||
{
|
|
||||||
return m_node != rhs.m_node;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
xml_node<Ch> *m_node;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
//! Iterator of child attributes of xml_node
|
|
||||||
template<class Ch>
|
|
||||||
class attribute_iterator
|
|
||||||
{
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
typedef typename xml_attribute<Ch> value_type;
|
|
||||||
typedef typename xml_attribute<Ch> &reference;
|
|
||||||
typedef typename xml_attribute<Ch> *pointer;
|
|
||||||
typedef std::ptrdiff_t difference_type;
|
|
||||||
typedef std::bidirectional_iterator_tag iterator_category;
|
|
||||||
|
|
||||||
attribute_iterator()
|
|
||||||
: m_attribute(0)
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
attribute_iterator(xml_node<Ch> *node)
|
|
||||||
: m_attribute(node->first_attribute())
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
reference operator *() const
|
|
||||||
{
|
|
||||||
assert(m_attribute);
|
|
||||||
return *m_attribute;
|
|
||||||
}
|
|
||||||
|
|
||||||
pointer operator->() const
|
|
||||||
{
|
|
||||||
assert(m_attribute);
|
|
||||||
return m_attribute;
|
|
||||||
}
|
|
||||||
|
|
||||||
attribute_iterator& operator++()
|
|
||||||
{
|
|
||||||
assert(m_attribute);
|
|
||||||
m_attribute = m_attribute->next_attribute();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
attribute_iterator operator++(int)
|
|
||||||
{
|
|
||||||
attribute_iterator tmp = *this;
|
|
||||||
++this;
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
attribute_iterator& operator--()
|
|
||||||
{
|
|
||||||
assert(m_attribute && m_attribute->previous_attribute());
|
|
||||||
m_attribute = m_attribute->previous_attribute();
|
|
||||||
return *this;
|
|
||||||
}
|
|
||||||
|
|
||||||
attribute_iterator operator--(int)
|
|
||||||
{
|
|
||||||
attribute_iterator tmp = *this;
|
|
||||||
++this;
|
|
||||||
return tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator ==(const attribute_iterator<Ch> &rhs)
|
|
||||||
{
|
|
||||||
return m_attribute == rhs.m_attribute;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool operator !=(const attribute_iterator<Ch> &rhs)
|
|
||||||
{
|
|
||||||
return m_attribute != rhs.m_attribute;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
xml_attribute<Ch> *m_attribute;
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,421 +0,0 @@
|
|||||||
#ifndef RAPIDXML_PRINT_HPP_INCLUDED
|
|
||||||
#define RAPIDXML_PRINT_HPP_INCLUDED
|
|
||||||
|
|
||||||
// Copyright (C) 2006, 2009 Marcin Kalicinski
|
|
||||||
// Version 1.13
|
|
||||||
// Revision $DateTime: 2009/05/13 01:46:17 $
|
|
||||||
//! \file rapidxml_print.hpp This file contains rapidxml printer implementation
|
|
||||||
|
|
||||||
#include "rapidxml.hpp"
|
|
||||||
|
|
||||||
// Only include streams if not disabled
|
|
||||||
#ifndef RAPIDXML_NO_STREAMS
|
|
||||||
#include <ostream>
|
|
||||||
#include <iterator>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
namespace rapidxml
|
|
||||||
{
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
// Printing flags
|
|
||||||
|
|
||||||
const int print_no_indenting = 0x1; //!< Printer flag instructing the printer to suppress indenting of XML. See print() function.
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////
|
|
||||||
// Internal
|
|
||||||
|
|
||||||
//! \cond internal
|
|
||||||
namespace internal
|
|
||||||
{
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Internal character operations
|
|
||||||
|
|
||||||
// Copy characters from given range to given output iterator
|
|
||||||
template<class OutIt, class Ch>
|
|
||||||
inline OutIt copy_chars(const Ch *begin, const Ch *end, OutIt out)
|
|
||||||
{
|
|
||||||
while (begin != end)
|
|
||||||
*out++ = *begin++;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy characters from given range to given output iterator and expand
|
|
||||||
// characters into references (< > ' " &)
|
|
||||||
template<class OutIt, class Ch>
|
|
||||||
inline OutIt copy_and_expand_chars(const Ch *begin, const Ch *end, Ch noexpand, OutIt out)
|
|
||||||
{
|
|
||||||
while (begin != end)
|
|
||||||
{
|
|
||||||
if (*begin == noexpand)
|
|
||||||
{
|
|
||||||
*out++ = *begin; // No expansion, copy character
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
switch (*begin)
|
|
||||||
{
|
|
||||||
case Ch('<'):
|
|
||||||
*out++ = Ch('&'); *out++ = Ch('l'); *out++ = Ch('t'); *out++ = Ch(';');
|
|
||||||
break;
|
|
||||||
case Ch('>'):
|
|
||||||
*out++ = Ch('&'); *out++ = Ch('g'); *out++ = Ch('t'); *out++ = Ch(';');
|
|
||||||
break;
|
|
||||||
case Ch('\''):
|
|
||||||
*out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('p'); *out++ = Ch('o'); *out++ = Ch('s'); *out++ = Ch(';');
|
|
||||||
break;
|
|
||||||
case Ch('"'):
|
|
||||||
*out++ = Ch('&'); *out++ = Ch('q'); *out++ = Ch('u'); *out++ = Ch('o'); *out++ = Ch('t'); *out++ = Ch(';');
|
|
||||||
break;
|
|
||||||
case Ch('&'):
|
|
||||||
*out++ = Ch('&'); *out++ = Ch('a'); *out++ = Ch('m'); *out++ = Ch('p'); *out++ = Ch(';');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
*out++ = *begin; // No expansion, copy character
|
|
||||||
}
|
|
||||||
}
|
|
||||||
++begin; // Step to next character
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fill given output iterator with repetitions of the same character
|
|
||||||
template<class OutIt, class Ch>
|
|
||||||
inline OutIt fill_chars(OutIt out, int n, Ch ch)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < n; ++i)
|
|
||||||
*out++ = ch;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Find character
|
|
||||||
template<class Ch, Ch ch>
|
|
||||||
inline bool find_char(const Ch *begin, const Ch *end)
|
|
||||||
{
|
|
||||||
while (begin != end)
|
|
||||||
if (*begin++ == ch)
|
|
||||||
return true;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Internal printing operations
|
|
||||||
|
|
||||||
// Print node
|
|
||||||
template<class OutIt, class Ch>
|
|
||||||
inline OutIt print_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
|
||||||
{
|
|
||||||
// Print proper node type
|
|
||||||
switch (node->type())
|
|
||||||
{
|
|
||||||
|
|
||||||
// Document
|
|
||||||
case node_document:
|
|
||||||
out = print_children(out, node, flags, indent);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Element
|
|
||||||
case node_element:
|
|
||||||
out = print_element_node(out, node, flags, indent);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Data
|
|
||||||
case node_data:
|
|
||||||
out = print_data_node(out, node, flags, indent);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// CDATA
|
|
||||||
case node_cdata:
|
|
||||||
out = print_cdata_node(out, node, flags, indent);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Declaration
|
|
||||||
case node_declaration:
|
|
||||||
out = print_declaration_node(out, node, flags, indent);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Comment
|
|
||||||
case node_comment:
|
|
||||||
out = print_comment_node(out, node, flags, indent);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Doctype
|
|
||||||
case node_doctype:
|
|
||||||
out = print_doctype_node(out, node, flags, indent);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Pi
|
|
||||||
case node_pi:
|
|
||||||
out = print_pi_node(out, node, flags, indent);
|
|
||||||
break;
|
|
||||||
|
|
||||||
// Unknown
|
|
||||||
default:
|
|
||||||
assert(0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// If indenting not disabled, add line break after node
|
|
||||||
if (!(flags & print_no_indenting))
|
|
||||||
*out = Ch('\n'), ++out;
|
|
||||||
|
|
||||||
// Return modified iterator
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print children of the node
|
|
||||||
template<class OutIt, class Ch>
|
|
||||||
inline OutIt print_children(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
|
||||||
{
|
|
||||||
for (xml_node<Ch> *child = node->first_node(); child; child = child->next_sibling())
|
|
||||||
out = print_node(out, child, flags, indent);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print attributes of the node
|
|
||||||
template<class OutIt, class Ch>
|
|
||||||
inline OutIt print_attributes(OutIt out, const xml_node<Ch> *node, int flags)
|
|
||||||
{
|
|
||||||
for (xml_attribute<Ch> *attribute = node->first_attribute(); attribute; attribute = attribute->next_attribute())
|
|
||||||
{
|
|
||||||
if (attribute->name() && attribute->value())
|
|
||||||
{
|
|
||||||
// Print attribute name
|
|
||||||
*out = Ch(' '), ++out;
|
|
||||||
out = copy_chars(attribute->name(), attribute->name() + attribute->name_size(), out);
|
|
||||||
*out = Ch('='), ++out;
|
|
||||||
// Print attribute value using appropriate quote type
|
|
||||||
if (find_char<Ch, Ch('"')>(attribute->value(), attribute->value() + attribute->value_size()))
|
|
||||||
{
|
|
||||||
*out = Ch('\''), ++out;
|
|
||||||
out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('"'), out);
|
|
||||||
*out = Ch('\''), ++out;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
*out = Ch('"'), ++out;
|
|
||||||
out = copy_and_expand_chars(attribute->value(), attribute->value() + attribute->value_size(), Ch('\''), out);
|
|
||||||
*out = Ch('"'), ++out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print data node
|
|
||||||
template<class OutIt, class Ch>
|
|
||||||
inline OutIt print_data_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
|
||||||
{
|
|
||||||
assert(node->type() == node_data);
|
|
||||||
if (!(flags & print_no_indenting))
|
|
||||||
out = fill_chars(out, indent, Ch('\t'));
|
|
||||||
out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print data node
|
|
||||||
template<class OutIt, class Ch>
|
|
||||||
inline OutIt print_cdata_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
|
||||||
{
|
|
||||||
assert(node->type() == node_cdata);
|
|
||||||
if (!(flags & print_no_indenting))
|
|
||||||
out = fill_chars(out, indent, Ch('\t'));
|
|
||||||
*out = Ch('<'); ++out;
|
|
||||||
*out = Ch('!'); ++out;
|
|
||||||
*out = Ch('['); ++out;
|
|
||||||
*out = Ch('C'); ++out;
|
|
||||||
*out = Ch('D'); ++out;
|
|
||||||
*out = Ch('A'); ++out;
|
|
||||||
*out = Ch('T'); ++out;
|
|
||||||
*out = Ch('A'); ++out;
|
|
||||||
*out = Ch('['); ++out;
|
|
||||||
out = copy_chars(node->value(), node->value() + node->value_size(), out);
|
|
||||||
*out = Ch(']'); ++out;
|
|
||||||
*out = Ch(']'); ++out;
|
|
||||||
*out = Ch('>'); ++out;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print element node
|
|
||||||
template<class OutIt, class Ch>
|
|
||||||
inline OutIt print_element_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
|
||||||
{
|
|
||||||
assert(node->type() == node_element);
|
|
||||||
|
|
||||||
// Print element name and attributes, if any
|
|
||||||
if (!(flags & print_no_indenting))
|
|
||||||
out = fill_chars(out, indent, Ch('\t'));
|
|
||||||
*out = Ch('<'), ++out;
|
|
||||||
out = copy_chars(node->name(), node->name() + node->name_size(), out);
|
|
||||||
out = print_attributes(out, node, flags);
|
|
||||||
|
|
||||||
// If node is childless
|
|
||||||
if (node->value_size() == 0 && !node->first_node())
|
|
||||||
{
|
|
||||||
// Print childless node tag ending
|
|
||||||
*out = Ch('/'), ++out;
|
|
||||||
*out = Ch('>'), ++out;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Print normal node tag ending
|
|
||||||
*out = Ch('>'), ++out;
|
|
||||||
|
|
||||||
// Test if node contains a single data node only (and no other nodes)
|
|
||||||
xml_node<Ch> *child = node->first_node();
|
|
||||||
if (!child)
|
|
||||||
{
|
|
||||||
// If node has no children, only print its value without indenting
|
|
||||||
out = copy_and_expand_chars(node->value(), node->value() + node->value_size(), Ch(0), out);
|
|
||||||
}
|
|
||||||
else if (child->next_sibling() == 0 && child->type() == node_data)
|
|
||||||
{
|
|
||||||
// If node has a sole data child, only print its value without indenting
|
|
||||||
out = copy_and_expand_chars(child->value(), child->value() + child->value_size(), Ch(0), out);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Print all children with full indenting
|
|
||||||
if (!(flags & print_no_indenting))
|
|
||||||
*out = Ch('\n'), ++out;
|
|
||||||
out = print_children(out, node, flags, indent + 1);
|
|
||||||
if (!(flags & print_no_indenting))
|
|
||||||
out = fill_chars(out, indent, Ch('\t'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print node end
|
|
||||||
*out = Ch('<'), ++out;
|
|
||||||
*out = Ch('/'), ++out;
|
|
||||||
out = copy_chars(node->name(), node->name() + node->name_size(), out);
|
|
||||||
*out = Ch('>'), ++out;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print declaration node
|
|
||||||
template<class OutIt, class Ch>
|
|
||||||
inline OutIt print_declaration_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
|
||||||
{
|
|
||||||
// Print declaration start
|
|
||||||
if (!(flags & print_no_indenting))
|
|
||||||
out = fill_chars(out, indent, Ch('\t'));
|
|
||||||
*out = Ch('<'), ++out;
|
|
||||||
*out = Ch('?'), ++out;
|
|
||||||
*out = Ch('x'), ++out;
|
|
||||||
*out = Ch('m'), ++out;
|
|
||||||
*out = Ch('l'), ++out;
|
|
||||||
|
|
||||||
// Print attributes
|
|
||||||
out = print_attributes(out, node, flags);
|
|
||||||
|
|
||||||
// Print declaration end
|
|
||||||
*out = Ch('?'), ++out;
|
|
||||||
*out = Ch('>'), ++out;
|
|
||||||
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print comment node
|
|
||||||
template<class OutIt, class Ch>
|
|
||||||
inline OutIt print_comment_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
|
||||||
{
|
|
||||||
assert(node->type() == node_comment);
|
|
||||||
if (!(flags & print_no_indenting))
|
|
||||||
out = fill_chars(out, indent, Ch('\t'));
|
|
||||||
*out = Ch('<'), ++out;
|
|
||||||
*out = Ch('!'), ++out;
|
|
||||||
*out = Ch('-'), ++out;
|
|
||||||
*out = Ch('-'), ++out;
|
|
||||||
out = copy_chars(node->value(), node->value() + node->value_size(), out);
|
|
||||||
*out = Ch('-'), ++out;
|
|
||||||
*out = Ch('-'), ++out;
|
|
||||||
*out = Ch('>'), ++out;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print doctype node
|
|
||||||
template<class OutIt, class Ch>
|
|
||||||
inline OutIt print_doctype_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
|
||||||
{
|
|
||||||
assert(node->type() == node_doctype);
|
|
||||||
if (!(flags & print_no_indenting))
|
|
||||||
out = fill_chars(out, indent, Ch('\t'));
|
|
||||||
*out = Ch('<'), ++out;
|
|
||||||
*out = Ch('!'), ++out;
|
|
||||||
*out = Ch('D'), ++out;
|
|
||||||
*out = Ch('O'), ++out;
|
|
||||||
*out = Ch('C'), ++out;
|
|
||||||
*out = Ch('T'), ++out;
|
|
||||||
*out = Ch('Y'), ++out;
|
|
||||||
*out = Ch('P'), ++out;
|
|
||||||
*out = Ch('E'), ++out;
|
|
||||||
*out = Ch(' '), ++out;
|
|
||||||
out = copy_chars(node->value(), node->value() + node->value_size(), out);
|
|
||||||
*out = Ch('>'), ++out;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Print pi node
|
|
||||||
template<class OutIt, class Ch>
|
|
||||||
inline OutIt print_pi_node(OutIt out, const xml_node<Ch> *node, int flags, int indent)
|
|
||||||
{
|
|
||||||
assert(node->type() == node_pi);
|
|
||||||
if (!(flags & print_no_indenting))
|
|
||||||
out = fill_chars(out, indent, Ch('\t'));
|
|
||||||
*out = Ch('<'), ++out;
|
|
||||||
*out = Ch('?'), ++out;
|
|
||||||
out = copy_chars(node->name(), node->name() + node->name_size(), out);
|
|
||||||
*out = Ch(' '), ++out;
|
|
||||||
out = copy_chars(node->value(), node->value() + node->value_size(), out);
|
|
||||||
*out = Ch('?'), ++out;
|
|
||||||
*out = Ch('>'), ++out;
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
//! \endcond
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////
|
|
||||||
// Printing
|
|
||||||
|
|
||||||
//! Prints XML to given output iterator.
|
|
||||||
//! \param out Output iterator to print to.
|
|
||||||
//! \param node Node to be printed. Pass xml_document to print entire document.
|
|
||||||
//! \param flags Flags controlling how XML is printed.
|
|
||||||
//! \return Output iterator pointing to position immediately after last character of printed text.
|
|
||||||
template<class OutIt, class Ch>
|
|
||||||
inline OutIt print(OutIt out, const xml_node<Ch> &node, int flags = 0)
|
|
||||||
{
|
|
||||||
return internal::print_node(out, &node, flags, 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
#ifndef RAPIDXML_NO_STREAMS
|
|
||||||
|
|
||||||
//! Prints XML to given output stream.
|
|
||||||
//! \param out Output stream to print to.
|
|
||||||
//! \param node Node to be printed. Pass xml_document to print entire document.
|
|
||||||
//! \param flags Flags controlling how XML is printed.
|
|
||||||
//! \return Output stream.
|
|
||||||
template<class Ch>
|
|
||||||
inline std::basic_ostream<Ch> &print(std::basic_ostream<Ch> &out, const xml_node<Ch> &node, int flags = 0)
|
|
||||||
{
|
|
||||||
print(std::ostream_iterator<Ch>(out), node, flags);
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Prints formatted XML to given output stream. Uses default printing flags. Use print() function to customize printing process.
|
|
||||||
//! \param out Output stream to print to.
|
|
||||||
//! \param node Node to be printed.
|
|
||||||
//! \return Output stream.
|
|
||||||
template<class Ch>
|
|
||||||
inline std::basic_ostream<Ch> &operator <<(std::basic_ostream<Ch> &out, const xml_node<Ch> &node)
|
|
||||||
{
|
|
||||||
return print(out, node);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
#ifndef RAPIDXML_UTILS_HPP_INCLUDED
|
|
||||||
#define RAPIDXML_UTILS_HPP_INCLUDED
|
|
||||||
|
|
||||||
// Copyright (C) 2006, 2009 Marcin Kalicinski
|
|
||||||
// Version 1.13
|
|
||||||
// Revision $DateTime: 2009/05/13 01:46:17 $
|
|
||||||
//! \file rapidxml_utils.hpp This file contains high-level rapidxml utilities that can be useful
|
|
||||||
//! in certain simple scenarios. They should probably not be used if maximizing performance is the main objective.
|
|
||||||
|
|
||||||
#include "rapidxml.hpp"
|
|
||||||
#include <vector>
|
|
||||||
#include <string>
|
|
||||||
#include <fstream>
|
|
||||||
#include <stdexcept>
|
|
||||||
|
|
||||||
namespace rapidxml
|
|
||||||
{
|
|
||||||
|
|
||||||
//! Represents data loaded from a file
|
|
||||||
template<class Ch = char>
|
|
||||||
class file
|
|
||||||
{
|
|
||||||
|
|
||||||
public:
|
|
||||||
|
|
||||||
//! Loads file into the memory. Data will be automatically destroyed by the destructor.
|
|
||||||
//! \param filename Filename to load.
|
|
||||||
file(const char *filename)
|
|
||||||
{
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
// Open stream
|
|
||||||
basic_ifstream<Ch> stream(filename, ios::binary);
|
|
||||||
if (!stream)
|
|
||||||
throw runtime_error(string("cannot open file ") + filename);
|
|
||||||
stream.unsetf(ios::skipws);
|
|
||||||
|
|
||||||
// Determine stream size
|
|
||||||
stream.seekg(0, ios::end);
|
|
||||||
size_t size = stream.tellg();
|
|
||||||
stream.seekg(0);
|
|
||||||
|
|
||||||
// Load data and add terminating 0
|
|
||||||
m_data.resize(size + 1);
|
|
||||||
stream.read(&m_data.front(), static_cast<streamsize>(size));
|
|
||||||
m_data[size] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Loads file into the memory. Data will be automatically destroyed by the destructor
|
|
||||||
//! \param stream Stream to load from
|
|
||||||
file(std::basic_istream<Ch> &stream)
|
|
||||||
{
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
// Load data and add terminating 0
|
|
||||||
stream.unsetf(ios::skipws);
|
|
||||||
m_data.assign(istreambuf_iterator<Ch>(stream), istreambuf_iterator<Ch>());
|
|
||||||
if (stream.fail() || stream.bad())
|
|
||||||
throw runtime_error("error reading stream");
|
|
||||||
m_data.push_back(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Gets file data.
|
|
||||||
//! \return Pointer to data of file.
|
|
||||||
Ch *data()
|
|
||||||
{
|
|
||||||
return &m_data.front();
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Gets file data.
|
|
||||||
//! \return Pointer to data of file.
|
|
||||||
const Ch *data() const
|
|
||||||
{
|
|
||||||
return &m_data.front();
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Gets file data size.
|
|
||||||
//! \return Size of file data, in characters.
|
|
||||||
std::size_t size() const
|
|
||||||
{
|
|
||||||
return m_data.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
std::vector<Ch> m_data; // File data
|
|
||||||
|
|
||||||
};
|
|
||||||
|
|
||||||
//! Counts children of node. Time complexity is O(n).
|
|
||||||
//! \return Number of children of node
|
|
||||||
template<class Ch>
|
|
||||||
inline std::size_t count_children(xml_node<Ch> *node)
|
|
||||||
{
|
|
||||||
xml_node<Ch> *child = node->first_node();
|
|
||||||
std::size_t count = 0;
|
|
||||||
while (child)
|
|
||||||
{
|
|
||||||
++count;
|
|
||||||
child = child->next_sibling();
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
//! Counts attributes of node. Time complexity is O(n).
|
|
||||||
//! \return Number of attributes of node
|
|
||||||
template<class Ch>
|
|
||||||
inline std::size_t count_attributes(xml_node<Ch> *node)
|
|
||||||
{
|
|
||||||
xml_attribute<Ch> *attr = node->first_attribute();
|
|
||||||
std::size_t count = 0;
|
|
||||||
while (attr)
|
|
||||||
{
|
|
||||||
++count;
|
|
||||||
attr = attr->next_attribute();
|
|
||||||
}
|
|
||||||
return count;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
#endif
|
|
||||||
@@ -1,9 +1,11 @@
|
|||||||
add_executable(tmx2gba
|
add_executable(tmx2gba
|
||||||
|
strtools.hpp strtools.cpp
|
||||||
argparse.hpp argparse.cpp
|
argparse.hpp argparse.cpp
|
||||||
$<$<NOT:$<TARGET_EXISTS:ZLIB::ZLIB>>:gzip.hpp gzip.cpp>
|
$<$<NOT:$<TARGET_EXISTS:ZLIB::ZLIB>>:gzip.hpp gzip.cpp>
|
||||||
tmxlayer.hpp
|
tmxlayer.hpp
|
||||||
tmxobject.hpp
|
tmxobject.hpp
|
||||||
tmxtileset.hpp
|
tmxtileset.hpp
|
||||||
|
tmxmap.hpp tmxmap.cpp
|
||||||
tmxreader.hpp tmxreader.cpp
|
tmxreader.hpp tmxreader.cpp
|
||||||
convert.hpp convert.cpp
|
convert.hpp convert.cpp
|
||||||
headerwriter.hpp headerwriter.cpp
|
headerwriter.hpp headerwriter.cpp
|
||||||
@@ -14,11 +16,12 @@ configure_file(config.h.in config.h @ONLY)
|
|||||||
target_sources(tmx2gba PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
target_sources(tmx2gba PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/config.h)
|
||||||
target_include_directories(tmx2gba PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
target_include_directories(tmx2gba PRIVATE ${CMAKE_CURRENT_BINARY_DIR})
|
||||||
|
|
||||||
set_target_properties(tmx2gba PROPERTIES CXX_STANDARD 20)
|
set_target_properties(tmx2gba PROPERTIES
|
||||||
|
CXX_STANDARD 20
|
||||||
|
CXX_STANDARD_REQUIRED ON)
|
||||||
|
|
||||||
target_compile_definitions(${PROJECT_NAME} PRIVATE
|
target_compile_definitions(${PROJECT_NAME} PRIVATE
|
||||||
$<$<BOOL:${MSVC}>:_CRT_SECURE_NO_WARNINGS> # disable msvc warning
|
$<$<BOOL:${MSVC}>:_CRT_SECURE_NO_WARNINGS>) # Disable msvc warning
|
||||||
$<$<TARGET_EXISTS:ZLIB::ZLIB>:USE_ZLIB>)
|
|
||||||
|
|
||||||
# Enable strong warnings
|
# Enable strong warnings
|
||||||
target_compile_options(tmx2gba PRIVATE
|
target_compile_options(tmx2gba PRIVATE
|
||||||
@@ -26,9 +29,8 @@ target_compile_options(tmx2gba PRIVATE
|
|||||||
$<$<CXX_COMPILER_ID:GNU,Clang,AppleClang>:-Wall -Wextra -pedantic>
|
$<$<CXX_COMPILER_ID:GNU,Clang,AppleClang>:-Wall -Wextra -pedantic>
|
||||||
$<$<CXX_COMPILER_ID:Clang,AppleClang>:-Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded>)
|
$<$<CXX_COMPILER_ID:Clang,AppleClang>:-Weverything -Wno-c++98-compat -Wno-c++98-compat-pedantic -Wno-padded>)
|
||||||
|
|
||||||
target_link_libraries(tmx2gba External::rapidxml)
|
target_link_libraries(tmx2gba
|
||||||
#pugixml)
|
pugixml::pugixml base64::base64 Zstd::Zstd
|
||||||
target_link_libraries(tmx2gba base64::base64 Zstd::Zstd
|
|
||||||
$<$<TARGET_EXISTS:ZLIB::ZLIB>:ZLIB::ZLIB>
|
$<$<TARGET_EXISTS:ZLIB::ZLIB>:ZLIB::ZLIB>
|
||||||
$<$<TARGET_EXISTS:miniz::miniz>:miniz::miniz>)
|
$<$<TARGET_EXISTS:miniz::miniz>:miniz::miniz>)
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
/* argparse.cpp - Copyright (C) 2024 a dinosaur (zlib, see COPYING.txt) */
|
// SPDX-License-Identifier: Zlib
|
||||||
|
// SPDX-FileCopyrightText: (c) 2024 a dinosaur
|
||||||
|
|
||||||
#include "argparse.hpp"
|
#include "argparse.hpp"
|
||||||
#include <optional>
|
#include <optional>
|
||||||
@@ -6,6 +7,7 @@
|
|||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
|
||||||
ArgParse::ArgParser::ArgParser(
|
ArgParse::ArgParser::ArgParser(
|
||||||
@@ -98,19 +100,18 @@ ArgParse::ParseCtrl ArgParse::ParserState::Next(const std::string_view token)
|
|||||||
{
|
{
|
||||||
flagChar = flag.value();
|
flagChar = flag.value();
|
||||||
const auto opt = getOption(flagChar);
|
const auto opt = getOption(flagChar);
|
||||||
if (opt.has_value())
|
if (!opt.has_value())
|
||||||
|
return ParseCtrl::QUIT_ERR_UNKNOWN;
|
||||||
|
bool expect = !opt.value().get().argumentName.empty();
|
||||||
|
if (token.length() <= 2)
|
||||||
{
|
{
|
||||||
bool expect = !opt.value().get().argumentName.empty();
|
expectArg = expect;
|
||||||
if (token.length() <= 2)
|
if (!expectArg)
|
||||||
{
|
return handler(flagChar, "");
|
||||||
expectArg = expect;
|
}
|
||||||
if (!expectArg)
|
else
|
||||||
return handler(flagChar, "");
|
{
|
||||||
}
|
return handler(flagChar, expect ? token.substr(2) : "");
|
||||||
else
|
|
||||||
{
|
|
||||||
return handler(flagChar, expect ? token.substr(2) : "");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (!token.empty())
|
else if (!token.empty())
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
/* argparse.hpp - Copyright (C) 2024 a dinosaur (zlib, see COPYING.txt) */
|
// SPDX-License-Identifier: Zlib
|
||||||
|
// SPDX-FileCopyrightText: (c) 2024 a dinosaur
|
||||||
|
|
||||||
#ifndef ARGPARSE_HPP
|
#ifndef ARGPARSE_HPP
|
||||||
#define ARGPARSE_HPP
|
#define ARGPARSE_HPP
|
||||||
|
|||||||
@@ -1,6 +1,10 @@
|
|||||||
#ifndef CONFIG_H
|
#ifndef CONFIG_H
|
||||||
#define CONFIG_H
|
#define CONFIG_H
|
||||||
|
|
||||||
#define TMX2GBA_VERSION "@PROJECT_VERSION@"
|
#define TMX2GBA_VERSION "@PROJECT_VERSION@"
|
||||||
|
#define TMX2GBA_DESCRIPTION "@PROJECT_DESCRIPTION@"
|
||||||
|
#define TMX2GBA_HOMEPAGE "@PROJECT_HOMEPAGE_URL@"
|
||||||
|
|
||||||
|
#cmakedefine USE_ZLIB
|
||||||
|
|
||||||
#endif//CONFIG_H
|
#endif//CONFIG_H
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
/* converter.hpp - Copyright (C) 2015-2024 a dinosaur (zlib, see COPYING.txt) */
|
// SPDX-License-Identifier: Zlib
|
||||||
|
// SPDX-FileCopyrightText: (c) 2015-2024 a dinosaur
|
||||||
|
|
||||||
#include "convert.hpp"
|
#include "convert.hpp"
|
||||||
#include "tmxreader.hpp"
|
#include "tmxreader.hpp"
|
||||||
@@ -12,8 +13,7 @@ bool convert::ConvertCharmap(std::vector<uint16_t>& out, int idxOffset, uint32_t
|
|||||||
|
|
||||||
const size_t numTiles = tmx.TileCount();
|
const size_t numTiles = tmx.TileCount();
|
||||||
assert(gfxTiles.size() == numTiles);
|
assert(gfxTiles.size() == numTiles);
|
||||||
if (palTiles.has_value())
|
assert(!palTiles.has_value() || palTiles.value().size() == numTiles);
|
||||||
assert(palTiles.value().size() == numTiles);
|
|
||||||
|
|
||||||
out.reserve(numTiles);
|
out.reserve(numTiles);
|
||||||
for (size_t i = 0; i < numTiles; ++i)
|
for (size_t i = 0; i < numTiles; ++i)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
/* converter.hpp - Copyright (C) 2024 a dinosaur (zlib, see COPYING.txt) */
|
// SPDX-License-Identifier: Zlib
|
||||||
|
// SPDX-FileCopyrightText: (c) 2024 a dinosaur
|
||||||
|
|
||||||
#ifndef CONVERT_HPP
|
#ifndef CONVERT_HPP
|
||||||
#define CONVERT_HPP
|
#define CONVERT_HPP
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// SPDX-License-Identifier: Zlib
|
// SPDX-License-Identifier: Zlib
|
||||||
// SPDX-FileCopyrightText: (c) 2024 a dinosaur
|
// SPDX-FileCopyrightText: (c) 2024 a dinosaur
|
||||||
|
|
||||||
#include "tmxlite/detail/gzip.hpp"
|
#include "gzip.hpp"
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
/* headerwriter.cpp - Copyright (C) 2015-2024 a dinosaur (zlib, see COPYING.txt) */
|
// SPDX-License-Identifier: Zlib
|
||||||
|
// SPDX-FileCopyrightText: (c) 2015-2024 a dinosaur
|
||||||
|
|
||||||
#include "headerwriter.hpp"
|
#include "headerwriter.hpp"
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
/* headerwriter.hpp - Copyright (C) 2015-2024 a dinosaur (zlib, see COPYING.txt) */
|
// SPDX-License-Identifier: Zlib
|
||||||
|
// SPDX-FileCopyrightText: (c) 2015-2024 a dinosaur
|
||||||
|
|
||||||
#ifndef HEADERWRITER_HPP
|
#ifndef HEADERWRITER_HPP
|
||||||
#define HEADERWRITER_HPP
|
#define HEADERWRITER_HPP
|
||||||
|
|||||||
37
src/strtools.cpp
Normal file
37
src/strtools.cpp
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// SPDX-License-Identifier: Zlib
|
||||||
|
// SPDX-FileCopyrightText: (c) 2015-2024 a dinosaur
|
||||||
|
|
||||||
|
#include "strtools.hpp"
|
||||||
|
#include <cctype>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
|
||||||
|
const std::string_view TrimWhitespace(const std::string_view str)
|
||||||
|
{
|
||||||
|
auto beg = std::find_if_not(str.begin(), str.end(), ::isspace);
|
||||||
|
if (beg == std::end(str))
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
auto end = std::find_if_not(str.rbegin(), str.rend(), ::isspace);
|
||||||
|
auto begOff = std::distance(str.begin(), beg);
|
||||||
|
auto endOff = std::distance(end, str.rend()) - begOff;
|
||||||
|
using size_type = std::string::size_type;
|
||||||
|
return str.substr(static_cast<size_type>(begOff), static_cast<size_type>(endOff));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SanitiseLabel(const std::string_view ident)
|
||||||
|
{
|
||||||
|
std::string out;
|
||||||
|
out.reserve(ident.length());
|
||||||
|
|
||||||
|
int last = '_';
|
||||||
|
for (int i : ident)
|
||||||
|
{
|
||||||
|
if (out.empty() && std::isdigit(i)) { continue; }
|
||||||
|
if (!std::isalnum(i)) { i = '_'; }
|
||||||
|
if (i != '_' || last != '_') { out.push_back(i); }
|
||||||
|
last = i;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
113
src/strtools.hpp
Normal file
113
src/strtools.hpp
Normal file
@@ -0,0 +1,113 @@
|
|||||||
|
// SPDX-License-Identifier: Zlib
|
||||||
|
// SPDX-FileCopyrightText: (c) 2024 a dinosaur
|
||||||
|
|
||||||
|
#ifndef STRTOOLS_HPP
|
||||||
|
#define STRTOOLS_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
// Cut leading & trailing whitespace (including newlines)
|
||||||
|
[[nodiscard]] const std::string_view TrimWhitespace(const std::string_view str);
|
||||||
|
|
||||||
|
// Convert string to valid C identifier
|
||||||
|
[[nodiscard]] std::string SanitiseLabel(const std::string_view ident);
|
||||||
|
|
||||||
|
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
// Template functions for converting unsigned ints to C/GNU style hex
|
||||||
|
|
||||||
|
static inline constexpr char CHexU(uint8_t h) { return "0123456789ABCDEF"[h >> 4]; }
|
||||||
|
static inline constexpr char CHexL(uint8_t l) { return "0123456789ABCDEF"[l & 15]; }
|
||||||
|
|
||||||
|
template <typename T> static void CHex(std::ostream& s, T x);
|
||||||
|
template <> void CHex(std::ostream& s, uint8_t x)
|
||||||
|
{
|
||||||
|
if (x > 9) s << "0x";
|
||||||
|
if (x > 15) s << CHexU(x);
|
||||||
|
s << CHexL(x);
|
||||||
|
}
|
||||||
|
template <> void CHex(std::ostream& s, uint16_t x)
|
||||||
|
{
|
||||||
|
if (x > 9) s << "0x";
|
||||||
|
if (x > 4095) s << CHexU(static_cast<uint8_t>(x >> 8));
|
||||||
|
if (x > 255) s << CHexL(static_cast<uint8_t>(x >> 8));
|
||||||
|
if (x > 15) s << CHexU(static_cast<uint8_t>(x));
|
||||||
|
s << CHexL(static_cast<uint8_t>(x));
|
||||||
|
}
|
||||||
|
template <> void CHex(std::ostream& s, uint32_t x)
|
||||||
|
{
|
||||||
|
if (x > 9) s << "0x";
|
||||||
|
if (x > 0xFFFFFFF) s << CHexU(static_cast<uint8_t>(x >> 24));
|
||||||
|
if (x > 0xFFFFFF) s << CHexL(static_cast<uint8_t>(x >> 24));
|
||||||
|
if (x > 0xFFFFF) s << CHexU(static_cast<uint8_t>(x >> 16));
|
||||||
|
if (x > 65535) s << CHexL(static_cast<uint8_t>(x >> 16));
|
||||||
|
if (x > 4095) s << CHexU(static_cast<uint8_t>(x >> 8));
|
||||||
|
if (x > 255) s << CHexL(static_cast<uint8_t>(x >> 8));
|
||||||
|
if (x > 15) s << CHexU(static_cast<uint8_t>(x));
|
||||||
|
s << CHexL(static_cast<uint8_t>(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
// Templated string to int/float w/ exception-less error handling
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] static std::optional<T> IntFromStr(const char* str, int base = 0) noexcept
|
||||||
|
{
|
||||||
|
using std::numeric_limits;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
char* end = nullptr;
|
||||||
|
long res = std::strtol(str, &end, base);
|
||||||
|
if (errno == ERANGE) { return std::nullopt; }
|
||||||
|
if (str == end) { return std::nullopt; }
|
||||||
|
if constexpr (sizeof(long) > sizeof(T))
|
||||||
|
{
|
||||||
|
if (res > numeric_limits<T>::max() || res < numeric_limits<T>::min())
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<T>(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] static std::optional<T> UintFromStr(const char* str, int base = 0) noexcept
|
||||||
|
{
|
||||||
|
using std::numeric_limits;
|
||||||
|
|
||||||
|
char* end = nullptr;
|
||||||
|
errno = 0;
|
||||||
|
unsigned long res = std::strtoul(str, &end, base);
|
||||||
|
if (errno == ERANGE) { return std::nullopt; }
|
||||||
|
if (str == end) { return std::nullopt; }
|
||||||
|
if constexpr (numeric_limits<unsigned long>::max() > numeric_limits<T>::max())
|
||||||
|
{
|
||||||
|
if (res > numeric_limits<T>::max()) { return std::nullopt; }
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<T>(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] static std::optional<T> FloatFromStr(const char* str) noexcept
|
||||||
|
{
|
||||||
|
char* end = nullptr;
|
||||||
|
T res;
|
||||||
|
errno = 0;
|
||||||
|
if constexpr (std::is_same_v<T, float>)
|
||||||
|
res = std::strtof(str, &end);
|
||||||
|
else
|
||||||
|
res = static_cast<T>(std::strtod(str, &end));
|
||||||
|
if (errno == ERANGE) { return std::nullopt; }
|
||||||
|
if (str == end) { return std::nullopt; }
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif//STRTOOLS_HPP
|
||||||
@@ -1,82 +1,11 @@
|
|||||||
/* swwriter.cpp - Copyright (C) 2024 a dinosaur (zlib, see COPYING.txt) */
|
// SPDX-License-Identifier: Zlib
|
||||||
|
// SPDX-FileCopyrightText: (c) 2024 a dinosaur
|
||||||
|
|
||||||
#include "swriter.hpp"
|
#include "swriter.hpp"
|
||||||
|
#include "strtools.hpp"
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <limits>
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#define GNU_STYLE 0
|
|
||||||
#define MASM_STYLE 1
|
|
||||||
|
|
||||||
#define HEX_STYLE GNU_STYLE
|
|
||||||
|
|
||||||
|
|
||||||
static inline constexpr char HexU(uint8_t h) { return "0123456789ABCDEF"[h >> 4]; }
|
|
||||||
static inline constexpr char HexL(uint8_t l) { return "0123456789ABCDEF"[l & 15]; }
|
|
||||||
|
|
||||||
#if HEX_STYLE == GNU_STYLE
|
|
||||||
template <typename T> static void CHex(std::ostream& s, T x);
|
|
||||||
template <> void CHex(std::ostream& s, uint8_t x)
|
|
||||||
{
|
|
||||||
if (x > 9) s << "0x";
|
|
||||||
if (x > 15) s << HexU(x);
|
|
||||||
s << HexL(x);
|
|
||||||
}
|
|
||||||
template <> void CHex(std::ostream& s, uint16_t x)
|
|
||||||
{
|
|
||||||
if (x > 9) s << "0x";
|
|
||||||
if (x > 4095) s << HexU(static_cast<uint8_t>(x >> 8));
|
|
||||||
if (x > 255) s << HexL(static_cast<uint8_t>(x >> 8));
|
|
||||||
if (x > 15) s << HexU(static_cast<uint8_t>(x));
|
|
||||||
s << HexL(static_cast<uint8_t>(x));
|
|
||||||
}
|
|
||||||
template <> void CHex(std::ostream& s, uint32_t x)
|
|
||||||
{
|
|
||||||
if (x > 9) s << "0x";
|
|
||||||
if (x > 0xFFFFFFF) s << HexU(static_cast<uint8_t>(x >> 24));
|
|
||||||
if (x > 0xFFFFFF) s << HexL(static_cast<uint8_t>(x >> 24));
|
|
||||||
if (x > 0xFFFFF) s << HexU(static_cast<uint8_t>(x >> 16));
|
|
||||||
if (x > 65535) s << HexL(static_cast<uint8_t>(x >> 16));
|
|
||||||
if (x > 4095) s << HexU(static_cast<uint8_t>(x >> 8));
|
|
||||||
if (x > 255) s << HexL(static_cast<uint8_t>(x >> 8));
|
|
||||||
if (x > 15) s << HexU(static_cast<uint8_t>(x));
|
|
||||||
s << HexL(static_cast<uint8_t>(x));
|
|
||||||
}
|
|
||||||
#elif HEX_STYLE == MASM_STYLE
|
|
||||||
template <typename T> static void MHex(std::ostream& s, T x);
|
|
||||||
template <> void MHex(std::ostream& s, uint8_t x)
|
|
||||||
{
|
|
||||||
if (x > 159) s << "0";
|
|
||||||
if (x > 15) s << HexU(x); else if (x > 9) s << "0";
|
|
||||||
s << HexL(x);
|
|
||||||
if (x > 9) s << "h";
|
|
||||||
}
|
|
||||||
template <> void MHex(std::ostream& s, uint16_t x)
|
|
||||||
{
|
|
||||||
if (x > 40959) s << "0";
|
|
||||||
if (x > 4095) s << HexU(static_cast<uint8_t>(x >> 8)); else if (x > 2559) s << "0";
|
|
||||||
if (x > 255) s << HexL(static_cast<uint8_t>(x >> 8)); else if (x > 159) s << "0";
|
|
||||||
if (x > 15) s << HexU(static_cast<uint8_t>(x)); else if (x > 9) s << "0";
|
|
||||||
s << HexL(static_cast<uint8_t>(x));
|
|
||||||
if (x > 9) s << "h";
|
|
||||||
}
|
|
||||||
template <> void MHex(std::ostream& s, uint32_t x)
|
|
||||||
{
|
|
||||||
if (x > 0x9FFFFFFF) s << "0";
|
|
||||||
if (x > 0xFFFFFFF) s << HexU(static_cast<uint8_t>(x >> 24)); else if (x > 0x9FFFFFF) s << "0";
|
|
||||||
if (x > 0xFFFFFF) s << HexL(static_cast<uint8_t>(x >> 24)); else if (x > 0x9FFFFF) s << "0";
|
|
||||||
if (x > 0xFFFFF) s << HexU(static_cast<uint8_t>(x >> 16)); else if (x > 655359) s << "0";
|
|
||||||
if (x > 65535) s << HexL(static_cast<uint8_t>(x >> 16)); else if (x > 40959) s << "0";
|
|
||||||
if (x > 4095) s << HexU(static_cast<uint8_t>(x >> 8)); else if (x > 2559) s << "0";
|
|
||||||
if (x > 255) s << HexL(static_cast<uint8_t>(x >> 8)); else if (x > 159) s << "0";
|
|
||||||
if (x > 15) s << HexU(static_cast<uint8_t>(x)); else if (x > 9) s << "0";
|
|
||||||
s << HexL(static_cast<uint8_t>(x));
|
|
||||||
if (x > 9) s << "h";
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
# error "Unknown hex style"
|
|
||||||
#endif
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T> static constexpr const std::string_view DataType();
|
template <typename T> static constexpr const std::string_view DataType();
|
||||||
template <> constexpr const std::string_view DataType<uint8_t>() { return ".byte"; }
|
template <> constexpr const std::string_view DataType<uint8_t>() { return ".byte"; }
|
||||||
@@ -95,11 +24,7 @@ static void WriteArrayDetail(std::ostream& s, const I beg, const I end, int perC
|
|||||||
s << "\t" << DataType<Element>() << " ";
|
s << "\t" << DataType<Element>() << " ";
|
||||||
|
|
||||||
const Element e = *it;
|
const Element e = *it;
|
||||||
#if HEX_STYLE == MASM_STYLE
|
|
||||||
MHex(s, e);
|
|
||||||
#elif HEX_STYLE == GNU_STYLE
|
|
||||||
CHex(s, e);
|
CHex(s, e);
|
||||||
#endif
|
|
||||||
|
|
||||||
if (++it == end)
|
if (++it == end)
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
/* swwriter.hpp - Copyright (C) 2024 a dinosaur (zlib, see COPYING.txt) */
|
// SPDX-License-Identifier: Zlib
|
||||||
|
// SPDX-FileCopyrightText: (c) 2024 a dinosaur
|
||||||
|
|
||||||
#ifndef SWRITER_HPP
|
#ifndef SWRITER_HPP
|
||||||
#define SWRITER_HPP
|
#define SWRITER_HPP
|
||||||
|
|||||||
@@ -1,10 +1,16 @@
|
|||||||
/* tmx2gba.cpp - Copyright (C) 2015-2024 a dinosaur (zlib, see COPYING.txt) */
|
// tmx2gba.cpp - main entry point
|
||||||
|
// SPDX-License-Identifier: Zlib
|
||||||
|
// SPDX-FileCopyrightText: (c) 2015-2024 a dinosaur
|
||||||
|
|
||||||
|
#include <string_view>
|
||||||
|
constexpr std::string_view copyrightStr("(c) 2015-2024 a dinosaur");
|
||||||
|
|
||||||
#include "argparse.hpp"
|
#include "argparse.hpp"
|
||||||
#include "tmxreader.hpp"
|
#include "tmxreader.hpp"
|
||||||
#include "convert.hpp"
|
#include "convert.hpp"
|
||||||
#include "headerwriter.hpp"
|
#include "headerwriter.hpp"
|
||||||
#include "swriter.hpp"
|
#include "swriter.hpp"
|
||||||
|
#include "strtools.hpp"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <map>
|
#include <map>
|
||||||
@@ -19,15 +25,14 @@ struct Arguments
|
|||||||
int offset = 0;
|
int offset = 0;
|
||||||
int palette = 0;
|
int palette = 0;
|
||||||
std::vector<std::string> objMappings;
|
std::vector<std::string> objMappings;
|
||||||
bool help = false, showVersion = false;
|
bool help = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
using ArgParse::Option;
|
using ArgParse::Option;
|
||||||
|
|
||||||
static const ArgParse::Options options =
|
static const ArgParse::Options options =
|
||||||
{
|
{
|
||||||
Option::Optional('h', {}, "Display this help & command info"),
|
Option::Optional('h', {}, "Display help & command info"),
|
||||||
Option::Optional('v', {}, "Display version & quit"),
|
|
||||||
Option::Optional('l', "name", "Name of layer to use (default first layer in TMX)"),
|
Option::Optional('l', "name", "Name of layer to use (default first layer in TMX)"),
|
||||||
Option::Optional('y', "name", "Layer for palette mappings"),
|
Option::Optional('y', "name", "Layer for palette mappings"),
|
||||||
Option::Optional('c', "name", "Output a separate 8bit collision map of the specified layer"),
|
Option::Optional('c', "name", "Output a separate 8bit collision map of the specified layer"),
|
||||||
@@ -51,7 +56,6 @@ static bool ParseArgs(int argc, char** argv, Arguments& params)
|
|||||||
switch (opt)
|
switch (opt)
|
||||||
{
|
{
|
||||||
case 'h': params.help = true; return ParseCtrl::QUIT_EARLY;
|
case 'h': params.help = true; return ParseCtrl::QUIT_EARLY;
|
||||||
case 'v': params.showVersion = true; return ParseCtrl::QUIT_EARLY;
|
|
||||||
case 'l': params.layer = arg; return ParseCtrl::CONTINUE;
|
case 'l': params.layer = arg; return ParseCtrl::CONTINUE;
|
||||||
case 'c': params.collisionlay = arg; return ParseCtrl::CONTINUE;
|
case 'c': params.collisionlay = arg; return ParseCtrl::CONTINUE;
|
||||||
case 'y': params.paletteLay = arg; return ParseCtrl::CONTINUE;
|
case 'y': params.paletteLay = arg; return ParseCtrl::CONTINUE;
|
||||||
@@ -69,11 +73,8 @@ static bool ParseArgs(int argc, char** argv, Arguments& params)
|
|||||||
catch (std::out_of_range const&) { return ParseCtrl::QUIT_ERR_RANGE; }
|
catch (std::out_of_range const&) { return ParseCtrl::QUIT_ERR_RANGE; }
|
||||||
});
|
});
|
||||||
|
|
||||||
if (!parser.Parse(std::span(argv + 1, argc - 1)))
|
if (!parser.Parse(std::span(argv + 1, argc - 1))) { return false; }
|
||||||
return false;
|
if (params.help) { return true; }
|
||||||
|
|
||||||
if (params.help || params.showVersion)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!params.flagFile.empty())
|
if (!params.flagFile.empty())
|
||||||
{
|
{
|
||||||
@@ -115,41 +116,16 @@ static bool ParseArgs(int argc, char** argv, Arguments& params)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static std::string SanitiseLabel(const std::string_view ident)
|
|
||||||
{
|
|
||||||
std::string out;
|
|
||||||
out.reserve(ident.length());
|
|
||||||
|
|
||||||
int last = '_';
|
|
||||||
for (int i : ident)
|
|
||||||
{
|
|
||||||
if (out.empty() && std::isdigit(i))
|
|
||||||
continue;
|
|
||||||
if (!std::isalnum(i))
|
|
||||||
i = '_';
|
|
||||||
if (i != '_' || last != '_')
|
|
||||||
out.push_back(i);
|
|
||||||
last = i;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
Arguments p;
|
Arguments p;
|
||||||
if (!ParseArgs(argc, argv, p))
|
if (!ParseArgs(argc, argv, p)) { return 1; }
|
||||||
return 1;
|
|
||||||
if (p.help)
|
if (p.help)
|
||||||
{
|
{
|
||||||
|
std::cout << "tmx2gba v" << TMX2GBA_VERSION << ", " << copyrightStr << std::endl;
|
||||||
options.ShowHelpUsage(argv[0], std::cout);
|
options.ShowHelpUsage(argv[0], std::cout);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (p.showVersion)
|
|
||||||
{
|
|
||||||
std::cout << "tmx2gba version " << TMX2GBA_VERSION << ", (c) 2015-2024 a dinosaur" << std::endl;
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Object mappings
|
// Object mappings
|
||||||
std::map<std::string, uint32_t> objMapping;
|
std::map<std::string, uint32_t> objMapping;
|
||||||
@@ -222,8 +198,7 @@ int main(int argc, char** argv)
|
|||||||
// Convert to GBA-friendly charmap data
|
// Convert to GBA-friendly charmap data
|
||||||
{
|
{
|
||||||
std::vector<uint16_t> charDat;
|
std::vector<uint16_t> charDat;
|
||||||
if (!convert::ConvertCharmap(charDat, p.offset, p.palette, tmx))
|
if (!convert::ConvertCharmap(charDat, p.offset, p.palette, tmx)) { return 1; }
|
||||||
return 1;
|
|
||||||
|
|
||||||
// Write out charmap
|
// Write out charmap
|
||||||
outH.WriteSize(tmx.GetSize().width, tmx.GetSize().height);
|
outH.WriteSize(tmx.GetSize().width, tmx.GetSize().height);
|
||||||
@@ -235,8 +210,7 @@ int main(int argc, char** argv)
|
|||||||
if (tmx.HasCollisionTiles())
|
if (tmx.HasCollisionTiles())
|
||||||
{
|
{
|
||||||
std::vector<uint8_t> collisionDat;
|
std::vector<uint8_t> collisionDat;
|
||||||
if (!convert::ConvertCollision(collisionDat, tmx))
|
if (!convert::ConvertCollision(collisionDat, tmx)) { return 1; }
|
||||||
return 1;
|
|
||||||
|
|
||||||
outH.WriteCollision(collisionDat);
|
outH.WriteCollision(collisionDat);
|
||||||
outS.WriteArray("Collision", collisionDat, 32);
|
outS.WriteArray("Collision", collisionDat, 32);
|
||||||
@@ -245,8 +219,7 @@ int main(int argc, char** argv)
|
|||||||
if (tmx.HasObjects())
|
if (tmx.HasObjects())
|
||||||
{
|
{
|
||||||
std::vector<uint32_t> objDat;
|
std::vector<uint32_t> objDat;
|
||||||
if (!convert::ConvertObjects(objDat, tmx))
|
if (!convert::ConvertObjects(objDat, tmx)) { return 1; }
|
||||||
return 1;
|
|
||||||
|
|
||||||
outH.WriteObjects(objDat);
|
outH.WriteObjects(objDat);
|
||||||
outS.WriteArray("Objdat", objDat);
|
outS.WriteArray("Objdat", objDat);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
/* tmxlayer.hpp - Copyright (C) 2015-2024 a dinosaur (zlib, see COPYING.txt) */
|
// SPDX-License-Identifier: Zlib
|
||||||
|
// SPDX-FileCopyrightText: (c) 2015-2024 a dinosaur
|
||||||
|
|
||||||
#ifndef TMXLAYER_HPP
|
#ifndef TMXLAYER_HPP
|
||||||
#define TMXLAYER_HPP
|
#define TMXLAYER_HPP
|
||||||
@@ -25,7 +26,7 @@ public:
|
|||||||
TmxLayer(int width, int height, const std::string_view name, std::vector<uint32_t>&& tileDat) noexcept
|
TmxLayer(int width, int height, const std::string_view name, std::vector<uint32_t>&& tileDat) noexcept
|
||||||
: mName(name), mWidth(width), mHeight(height), mTileDat(std::move(tileDat)) {}
|
: mName(name), mWidth(width), mHeight(height), mTileDat(std::move(tileDat)) {}
|
||||||
|
|
||||||
[[nodiscard]] constexpr const std::string_view Name() const noexcept { return mName; }
|
[[nodiscard]] const std::string_view Name() const noexcept { return mName; }
|
||||||
[[nodiscard]] constexpr std::pair<int, int> TileCount() const noexcept { return { mWidth, mHeight }; }
|
[[nodiscard]] constexpr std::pair<int, int> TileCount() const noexcept { return { mWidth, mHeight }; }
|
||||||
[[nodiscard]] constexpr const std::span<const uint32_t> Tiles() const noexcept { return mTileDat; }
|
[[nodiscard]] constexpr const std::span<const uint32_t> Tiles() const noexcept { return mTileDat; }
|
||||||
};
|
};
|
||||||
|
|||||||
236
src/tmxmap.cpp
Normal file
236
src/tmxmap.cpp
Normal file
@@ -0,0 +1,236 @@
|
|||||||
|
// SPDX-License-Identifier: Zlib
|
||||||
|
// SPDX-FileCopyrightText: (c) 2015-2024 a dinosaur
|
||||||
|
|
||||||
|
#include "tmxmap.hpp"
|
||||||
|
#include "strtools.hpp"
|
||||||
|
#include "config.h"
|
||||||
|
#include <pugixml.hpp>
|
||||||
|
#include <base64.h>
|
||||||
|
#ifdef USE_ZLIB
|
||||||
|
# include <zlib.h>
|
||||||
|
#else
|
||||||
|
# include "gzip.hpp"
|
||||||
|
#endif
|
||||||
|
#include <zstd.h>
|
||||||
|
#include <cerrno>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
|
||||||
|
enum class Encoding { XML, BASE64, CSV, INVALID };
|
||||||
|
enum class Compression { NONE, GZIP, ZLIB, ZSTD, INVALID };
|
||||||
|
|
||||||
|
[[nodiscard]] static Encoding EncodingFromStr(const std::string_view str)
|
||||||
|
{
|
||||||
|
if (str.empty()) { return Encoding::XML; }
|
||||||
|
if (str == "base64") { return Encoding::BASE64; }
|
||||||
|
if (str == "csv") { return Encoding::CSV; }
|
||||||
|
return Encoding::INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
[[nodiscard]] static Compression CompressionFromStr(const std::string_view str)
|
||||||
|
{
|
||||||
|
if (str.empty()) { return Compression::NONE; }
|
||||||
|
if (str == "gzip") { return Compression::GZIP; }
|
||||||
|
if (str == "zlib") { return Compression::ZLIB; }
|
||||||
|
if (str == "zstd") { return Compression::ZSTD; }
|
||||||
|
return Compression::INVALID;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
[[nodiscard]] static bool DecodeBase64(
|
||||||
|
std::vector<uint32_t>& out, size_t numTiles,
|
||||||
|
const std::string_view base64, Compression compression)
|
||||||
|
{
|
||||||
|
auto decoded = base64_decode(TrimWhitespace(base64));
|
||||||
|
if (decoded.empty()) { return false; }
|
||||||
|
const std::span source(reinterpret_cast<const uint8_t*>(decoded.data()), decoded.size());
|
||||||
|
|
||||||
|
//FIXME: lmao what is big endian
|
||||||
|
switch (compression)
|
||||||
|
{
|
||||||
|
case Compression::GZIP:
|
||||||
|
#ifndef USE_ZLIB
|
||||||
|
{
|
||||||
|
out.resize(numTiles);
|
||||||
|
GZipReader reader;
|
||||||
|
if (!reader.OpenMemory(source) ||
|
||||||
|
!reader.Read({ reinterpret_cast<uint8_t*>(out.data()), sizeof(uint32_t) * numTiles }) ||
|
||||||
|
!reader.Check())
|
||||||
|
return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
case Compression::ZLIB:
|
||||||
|
{
|
||||||
|
out.resize(numTiles);
|
||||||
|
// Decompress gzip/zlib data with zlib/zlib data miniz
|
||||||
|
z_stream s =
|
||||||
|
{
|
||||||
|
.next_in = const_cast<Bytef*>(source.data()),
|
||||||
|
.avail_in = static_cast<unsigned int>(source.size()),
|
||||||
|
.next_out = reinterpret_cast<Bytef*>(out.data()),
|
||||||
|
.avail_out = static_cast<unsigned int>(sizeof(uint32_t) * numTiles),
|
||||||
|
.zalloc = nullptr, .zfree = nullptr, .opaque = nullptr
|
||||||
|
};
|
||||||
|
#ifdef USE_ZLIB
|
||||||
|
const int wbits = (compression == Compression::GZIP) ? MAX_WBITS | 16 : MAX_WBITS;
|
||||||
|
#else
|
||||||
|
const int wbits = MZ_DEFAULT_WINDOW_BITS;
|
||||||
|
#endif
|
||||||
|
if (inflateInit2(&s, wbits) != Z_OK)
|
||||||
|
return false;
|
||||||
|
int res = inflate(&s, Z_FINISH);
|
||||||
|
inflateEnd(&s);
|
||||||
|
return res == Z_STREAM_END;
|
||||||
|
}
|
||||||
|
case Compression::ZSTD:
|
||||||
|
{
|
||||||
|
out.resize(numTiles);
|
||||||
|
auto res = ZSTD_decompress(
|
||||||
|
reinterpret_cast<void*>(out.data()),
|
||||||
|
sizeof(uint32_t) * numTiles,
|
||||||
|
source.data(), source.size());
|
||||||
|
return !ZSTD_isError(res);
|
||||||
|
}
|
||||||
|
case Compression::NONE:
|
||||||
|
{
|
||||||
|
out.reserve(numTiles);
|
||||||
|
const auto end = source.end();
|
||||||
|
for (auto it = source.begin(); it < end - 3;)
|
||||||
|
{
|
||||||
|
uint32_t tile = *it++;
|
||||||
|
tile |= static_cast<uint32_t>(*it++) << 8u;
|
||||||
|
tile |= static_cast<uint32_t>(*it++) << 16u;
|
||||||
|
tile |= static_cast<uint32_t>(*it++) << 24u;
|
||||||
|
out.emplace_back(tile);
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case Compression::INVALID:
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void TmxMap::ReadTileset(const pugi::xml_node& xNode)
|
||||||
|
{
|
||||||
|
std::string_view name = xNode.attribute("name").value();
|
||||||
|
std::string_view source = xNode.attribute("source").value();
|
||||||
|
|
||||||
|
auto firstGid = UintFromStr<uint32_t>(xNode.attribute("firstgid").value()).value_or(0);
|
||||||
|
auto numTiles = UintFromStr<uint32_t>(xNode.attribute("tilecount").value()).value_or(0);
|
||||||
|
if (numTiles == 0)
|
||||||
|
return; // FIXME: warn about empty tilesets or something
|
||||||
|
|
||||||
|
mTilesets.emplace_back(TmxTileset(name, source, firstGid, numTiles));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TmxMap::ReadLayer(const pugi::xml_node& xNode)
|
||||||
|
{
|
||||||
|
std::string_view name = xNode.attribute("name").value();
|
||||||
|
|
||||||
|
// Read layer size
|
||||||
|
int width = IntFromStr<int>(xNode.attribute("width").value()).value_or(0);
|
||||||
|
int height = IntFromStr<int>(xNode.attribute("height").value()).value_or(0);
|
||||||
|
if (width <= 0 || height <= 0) { return; }
|
||||||
|
const auto numTiles = static_cast<size_t>(width) * static_cast<size_t>(height);
|
||||||
|
|
||||||
|
auto xData = xNode.child("data");
|
||||||
|
if (xData.empty() || xData.first_child().empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Read data
|
||||||
|
std::vector<uint32_t> tileDat;
|
||||||
|
auto encoding = EncodingFromStr(xData.attribute("encoding").value());
|
||||||
|
if (encoding == Encoding::BASE64)
|
||||||
|
{
|
||||||
|
const std::string_view base64(xData.child_value());
|
||||||
|
if (base64.empty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
const auto compression = CompressionFromStr(xData.attribute("compression").value());
|
||||||
|
if (compression == Compression::INVALID || !DecodeBase64(tileDat, numTiles, base64, compression))
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (encoding == Encoding::XML)
|
||||||
|
{
|
||||||
|
tileDat.reserve(numTiles);
|
||||||
|
std::ranges::transform(xData.children("tile"), std::back_inserter(tileDat), [](auto it)
|
||||||
|
-> uint32_t { return UintFromStr<uint32_t>(it.attribute("gid").value()).value_or(0); });
|
||||||
|
}
|
||||||
|
else if (encoding == Encoding::CSV)
|
||||||
|
{
|
||||||
|
tileDat.reserve(numTiles);
|
||||||
|
const std::string_view csv(xData.child_value());
|
||||||
|
|
||||||
|
std::string::size_type pos = 0;
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
// TODO: check if this has a problem on other locales?
|
||||||
|
auto gid = UintFromStr<uint32_t>(csv.substr(pos).data());
|
||||||
|
if (gid.has_value())
|
||||||
|
tileDat.emplace_back(gid.value());
|
||||||
|
|
||||||
|
if ((pos = csv.find(',', pos)) == std::string::npos)
|
||||||
|
break;
|
||||||
|
++pos;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else { return; }
|
||||||
|
|
||||||
|
mLayers.emplace_back(TmxLayer(width, height, name, std::move(tileDat)));
|
||||||
|
}
|
||||||
|
|
||||||
|
void TmxMap::ReadObjectGroup(const pugi::xml_node& xNode)
|
||||||
|
{
|
||||||
|
std::string_view name(xNode.value());
|
||||||
|
std::vector<TmxObject> objects;
|
||||||
|
|
||||||
|
const auto xObjects = xNode.children("object");
|
||||||
|
//mObjects.reserve(xObjects.size())
|
||||||
|
for (const auto it : xObjects)
|
||||||
|
{
|
||||||
|
int id = IntFromStr<int>(it.attribute("id").value()).value_or(0);
|
||||||
|
std::string_view name = it.attribute("name").value();
|
||||||
|
|
||||||
|
// Read axis-aligned bounding box
|
||||||
|
auto x = FloatFromStr<float>(it.attribute("x").value()).value_or(0.0f);
|
||||||
|
auto y = FloatFromStr<float>(it.attribute("y").value()).value_or(0.0f);
|
||||||
|
auto width = FloatFromStr<float>(it.attribute("width").value()).value_or(0.0f);
|
||||||
|
auto height = FloatFromStr<float>(it.attribute("height").value()).value_or(0.0f);
|
||||||
|
|
||||||
|
objects.emplace_back(TmxObject(id, name, { x, y, width, height }));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (objects.empty())
|
||||||
|
return; //FIXME: log this
|
||||||
|
mObjectGroups.emplace_back(TmxObjectGroup(name, std::move(objects)));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool TmxMap::Load(const std::string& inPath)
|
||||||
|
{
|
||||||
|
// Parse document
|
||||||
|
pugi::xml_document xDoc;
|
||||||
|
auto res = xDoc.load_file(inPath.c_str());
|
||||||
|
if (res.status != pugi::xml_parse_status::status_ok)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Get map node
|
||||||
|
auto xMap = xDoc.child("map");
|
||||||
|
if (xMap.empty())
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Read map attribs
|
||||||
|
mWidth = IntFromStr<int>(xMap.attribute("width").value()).value_or(0);
|
||||||
|
mHeight = IntFromStr<int>(xMap.attribute("height").value()).value_or(0);
|
||||||
|
|
||||||
|
// Read nodes
|
||||||
|
for (auto it : xMap.children())
|
||||||
|
{
|
||||||
|
std::string_view name(it.name());
|
||||||
|
if (!name.compare("layer")) { ReadLayer(it); }
|
||||||
|
else if (!name.compare("tileset")) { ReadTileset(it); }
|
||||||
|
else if (!name.compare("objectgroup")) { ReadObjectGroup(it); }
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
38
src/tmxmap.hpp
Normal file
38
src/tmxmap.hpp
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
// SPDX-License-Identifier: Zlib
|
||||||
|
// SPDX-FileCopyrightText: (c) 2015-2024 a dinosaur
|
||||||
|
|
||||||
|
#ifndef TMXMAP_HPP
|
||||||
|
#define TMXMAP_HPP
|
||||||
|
|
||||||
|
#include "tmxtileset.hpp"
|
||||||
|
#include "tmxobject.hpp"
|
||||||
|
#include "tmxlayer.hpp"
|
||||||
|
#include <vector>
|
||||||
|
#include <span>
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
namespace pugi { class xml_node; }
|
||||||
|
|
||||||
|
class TmxMap
|
||||||
|
{
|
||||||
|
int mWidth = 0, mHeight = 0;
|
||||||
|
|
||||||
|
std::vector<TmxLayer> mLayers;
|
||||||
|
std::vector<TmxTileset> mTilesets;
|
||||||
|
std::vector<TmxObjectGroup> mObjectGroups;
|
||||||
|
|
||||||
|
void ReadTileset(const pugi::xml_node& xNode);
|
||||||
|
void ReadLayer(const pugi::xml_node& xNode);
|
||||||
|
void ReadObjectGroup(const pugi::xml_node& xNode);
|
||||||
|
|
||||||
|
public:
|
||||||
|
[[nodiscard]] bool Load(const std::string& inPath);
|
||||||
|
|
||||||
|
[[nodiscard]] constexpr std::pair<int, int> TileCount() const noexcept { return { mWidth, mHeight }; }
|
||||||
|
[[nodiscard]] constexpr const std::vector<TmxTileset>& Tilesets() const noexcept { return mTilesets; }
|
||||||
|
[[nodiscard]] constexpr const std::vector<TmxLayer>& Layers() const noexcept { return mLayers; }
|
||||||
|
[[nodiscard]] constexpr const std::vector<TmxObjectGroup>& ObjectGroups() const noexcept { return mObjectGroups; }
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif//TMXMAP_HPP
|
||||||
@@ -1,25 +1,43 @@
|
|||||||
/* tmxobject.hpp - Copyright (C) 2015-2024 a dinosaur (zlib, see COPYING.txt) */
|
// SPDX-License-Identifier: Zlib
|
||||||
|
// SPDX-FileCopyrightText: (c) 2015-2024 a dinosaur
|
||||||
|
|
||||||
#ifndef TMXOBJECT_HPP
|
#ifndef TMXOBJECT_HPP
|
||||||
#define TMXOBJECT_HPP
|
#define TMXOBJECT_HPP
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
#include <vector>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
|
|
||||||
class TmxObject
|
class TmxObject
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
TmxObject(std::string_view name, float x, float y) : mName(name), mPos{ x, y } {}
|
|
||||||
|
|
||||||
template <typename T>
|
template <typename T>
|
||||||
struct Position { T x, y; };
|
struct AABB { T x, y, w, h; };
|
||||||
|
|
||||||
constexpr const std::string_view Name() const noexcept { return mName; }
|
TmxObject(int id, std::string_view name, AABB<float>&& box) : mId(id), mName(name), mBox(std::move(box)) {}
|
||||||
constexpr Position<float> Pos() const noexcept { return mPos; }
|
|
||||||
|
constexpr int Id() const noexcept { return mId; }
|
||||||
|
const std::string_view Name() const noexcept { return mName; }
|
||||||
|
constexpr const AABB<float>& Box() const noexcept { return mBox; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
int mId;
|
||||||
std::string mName;
|
std::string mName;
|
||||||
Position<float> mPos;
|
AABB<float> mBox;
|
||||||
|
};
|
||||||
|
|
||||||
|
class TmxObjectGroup
|
||||||
|
{
|
||||||
|
std::string mName;
|
||||||
|
std::vector<TmxObject> mObjects;
|
||||||
|
|
||||||
|
public:
|
||||||
|
TmxObjectGroup(std::string_view name, std::vector<TmxObject>&& objects)
|
||||||
|
: mName(name), mObjects(std::move(objects)) {}
|
||||||
|
|
||||||
|
const std::string_view Name() const noexcept { return mName; }
|
||||||
|
constexpr const std::vector<TmxObject>& Objects() const noexcept { return mObjects; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif//TMXOBJECT_HPP
|
#endif//TMXOBJECT_HPP
|
||||||
|
|||||||
@@ -1,201 +1,13 @@
|
|||||||
/* tmxreader.cpp - Copyright (C) 2015-2024 a dinosaur (zlib, see COPYING.txt) */
|
// SPDX-License-Identifier: Zlib
|
||||||
|
// SPDX-FileCopyrightText: (c) 2015-2024 a dinosaur
|
||||||
|
|
||||||
#include "tmxreader.hpp"
|
#include "tmxreader.hpp"
|
||||||
#include "tmxtileset.hpp"
|
#include "tmxmap.hpp"
|
||||||
#include "tmxobject.hpp"
|
|
||||||
#include "tmxlayer.hpp"
|
|
||||||
#include "base64.h"
|
|
||||||
#ifdef USE_ZLIB
|
|
||||||
# include <zlib.h>
|
|
||||||
#else
|
|
||||||
# include "gzip.hpp"
|
|
||||||
#endif
|
|
||||||
#include <rapidxml/rapidxml.hpp>
|
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <ranges>
|
#include <ranges>
|
||||||
#include <sstream>
|
|
||||||
#include <fstream>
|
|
||||||
|
|
||||||
|
|
||||||
class TmxMap
|
|
||||||
{
|
|
||||||
int mWidth = 0, mHeight = 0;
|
|
||||||
|
|
||||||
std::vector<TmxLayer> mLayers;
|
|
||||||
std::vector<TmxTileset> mTilesets;
|
|
||||||
std::vector<TmxObject> mObjects;
|
|
||||||
|
|
||||||
[[nodiscard]] bool Decode(std::span<uint32_t> out, const std::string_view base64);
|
|
||||||
void ReadTileset(rapidxml::xml_node<>* aXNode);
|
|
||||||
void ReadLayer(rapidxml::xml_node<>* aXNode);
|
|
||||||
void ReadObjects(rapidxml::xml_node<>* aXNode);
|
|
||||||
|
|
||||||
public:
|
|
||||||
[[nodiscard]] bool Load(const std::string_view inPath);
|
|
||||||
|
|
||||||
constexpr std::pair<int, int> TileCount() const noexcept { return { mWidth, mHeight }; }
|
|
||||||
constexpr const std::vector<TmxTileset>& Tilesets() const noexcept { return mTilesets; }
|
|
||||||
constexpr const std::vector<TmxLayer>& Layers() const noexcept { return mLayers; }
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
bool TmxMap::Decode(std::span<uint32_t> out, const std::string_view base64)
|
|
||||||
{
|
|
||||||
// Cut leading & trailing whitespace (including newlines)
|
|
||||||
auto beg = std::find_if_not(base64.begin(), base64.end(), ::isspace);
|
|
||||||
if (beg == std::end(base64))
|
|
||||||
return false;
|
|
||||||
auto end = std::find_if_not(base64.rbegin(), base64.rend(), ::isspace);
|
|
||||||
std::size_t begOff = std::distance(base64.begin(), beg);
|
|
||||||
std::size_t endOff = std::distance(end, base64.rend()) - begOff;
|
|
||||||
const auto trimmed = base64.substr(begOff, endOff);
|
|
||||||
|
|
||||||
// Decode base64 string
|
|
||||||
std::string decoded = base64_decode(trimmed);
|
|
||||||
|
|
||||||
// Decompress compressed data
|
|
||||||
auto dstSize = static_cast<uLongf>(sizeof(uint32_t) * out.size());
|
|
||||||
int res = uncompress(
|
|
||||||
reinterpret_cast<unsigned char*>(out.data()),
|
|
||||||
&dstSize,
|
|
||||||
reinterpret_cast<const unsigned char*>(decoded.data()),
|
|
||||||
static_cast<uLong>(decoded.size()));
|
|
||||||
|
|
||||||
return res >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
void TmxMap::ReadTileset(rapidxml::xml_node<>* aXNode)
|
|
||||||
{
|
|
||||||
std::string_view name, source;
|
|
||||||
uint32_t firstGid = 0, lastGid = 0;
|
|
||||||
|
|
||||||
// Read name
|
|
||||||
auto xAttrib = aXNode->first_attribute("name");
|
|
||||||
if (xAttrib != nullptr)
|
|
||||||
name = xAttrib->value();
|
|
||||||
|
|
||||||
// Read source
|
|
||||||
xAttrib = aXNode->first_attribute("source");
|
|
||||||
if (xAttrib != nullptr)
|
|
||||||
source = xAttrib->value();
|
|
||||||
|
|
||||||
// Read first global ID
|
|
||||||
xAttrib = aXNode->first_attribute("firstgid");
|
|
||||||
if (xAttrib != nullptr)
|
|
||||||
firstGid = static_cast<uint32_t>(std::stoul(xAttrib->value()));
|
|
||||||
|
|
||||||
// Read last global ID
|
|
||||||
xAttrib = aXNode->first_attribute("lastgid");
|
|
||||||
if (xAttrib)
|
|
||||||
lastGid = static_cast<uint32_t>(std::stoul(xAttrib->value()));
|
|
||||||
|
|
||||||
mTilesets.emplace_back(TmxTileset(name, source, firstGid, lastGid));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TmxMap::ReadLayer(rapidxml::xml_node<>* aXNode)
|
|
||||||
{
|
|
||||||
std::string_view name;
|
|
||||||
int width = 0, height = 0;
|
|
||||||
|
|
||||||
// Read name
|
|
||||||
auto xAttrib = aXNode->first_attribute("name");
|
|
||||||
if (xAttrib != nullptr)
|
|
||||||
name = xAttrib->value();
|
|
||||||
|
|
||||||
// Read width
|
|
||||||
xAttrib = aXNode->first_attribute("width");
|
|
||||||
if (xAttrib != nullptr)
|
|
||||||
width = std::stoi(xAttrib->value());
|
|
||||||
|
|
||||||
// Read height
|
|
||||||
xAttrib = aXNode->first_attribute("height");
|
|
||||||
if (xAttrib != nullptr)
|
|
||||||
height = std::stoi(xAttrib->value());
|
|
||||||
|
|
||||||
// Read tile data
|
|
||||||
auto xData = aXNode->first_node("data");
|
|
||||||
if (xData == nullptr)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// TODO: don't assume base64
|
|
||||||
std::vector<uint32_t> tileDat(width * height);
|
|
||||||
if (!Decode(tileDat, xData->value()))
|
|
||||||
return;
|
|
||||||
|
|
||||||
mLayers.emplace_back(TmxLayer(width, height, name, std::move(tileDat)));
|
|
||||||
}
|
|
||||||
|
|
||||||
void TmxMap::ReadObjects(rapidxml::xml_node<>* aXNode)
|
|
||||||
{
|
|
||||||
for (auto xNode = aXNode->first_node(); xNode != nullptr; xNode = xNode->next_sibling())
|
|
||||||
{
|
|
||||||
if (strcmp(xNode->name(), "object") != 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
std::string_view name;
|
|
||||||
float x = 0.0f, y = 0.0f;
|
|
||||||
|
|
||||||
// Read name
|
|
||||||
auto xAttrib = xNode->first_attribute("name");
|
|
||||||
if (xAttrib != nullptr)
|
|
||||||
name = xAttrib->value();
|
|
||||||
|
|
||||||
// Read X pos
|
|
||||||
xAttrib = xNode->first_attribute("x");
|
|
||||||
if (xAttrib != nullptr)
|
|
||||||
x = std::stof(xAttrib->value());
|
|
||||||
|
|
||||||
// Read Y pos
|
|
||||||
xAttrib = xNode->first_attribute("y");
|
|
||||||
if (xAttrib != nullptr)
|
|
||||||
y = std::stof(xAttrib->value());
|
|
||||||
|
|
||||||
mObjects.emplace_back(TmxObject(name, x, y));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool TmxMap::Load(const std::string_view inPath)
|
|
||||||
{
|
|
||||||
// Read file into a buffer
|
|
||||||
auto inFile = std::ifstream(inPath);
|
|
||||||
std::stringstream buf;
|
|
||||||
buf << inFile.rdbuf();
|
|
||||||
std::string strXml = buf.str();
|
|
||||||
buf.clear();
|
|
||||||
|
|
||||||
// Parse document
|
|
||||||
rapidxml::xml_document<> xDoc;
|
|
||||||
xDoc.parse<0>(const_cast<char*>(strXml.c_str()));
|
|
||||||
|
|
||||||
// Get map node
|
|
||||||
auto xMap = xDoc.first_node("map");
|
|
||||||
if (xMap == nullptr)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Read map attribs
|
|
||||||
rapidxml::xml_attribute<>* xAttrib = nullptr;
|
|
||||||
if ((xAttrib = xMap->first_attribute("width")) != nullptr)
|
|
||||||
mWidth = std::stoi(xAttrib->value());
|
|
||||||
if ((xAttrib = xMap->first_attribute("height")) != nullptr)
|
|
||||||
mHeight = std::stoi(xAttrib->value());
|
|
||||||
|
|
||||||
// Read nodes
|
|
||||||
for (auto xNode = xMap->first_node(); xNode != nullptr; xNode = xNode->next_sibling())
|
|
||||||
{
|
|
||||||
// Read layer nodes
|
|
||||||
const auto xName = xNode->name();
|
|
||||||
if (std::strcmp(xName, "layer") == 0)
|
|
||||||
ReadLayer(xNode);
|
|
||||||
else if (std::strcmp(xName, "tileset") == 0)
|
|
||||||
ReadTileset(xNode);
|
|
||||||
else if (std::strcmp(xName, "objectgroup") == 0)
|
|
||||||
ReadObjects(xNode);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
TmxReader::Error TmxReader::Open(const std::string& inPath,
|
TmxReader::Error TmxReader::Open(const std::string& inPath,
|
||||||
const std::string_view graphicsName,
|
const std::string_view graphicsName,
|
||||||
const std::string_view paletteName,
|
const std::string_view paletteName,
|
||||||
@@ -209,10 +21,7 @@ TmxReader::Error TmxReader::Open(const std::string& inPath,
|
|||||||
using std::optional;
|
using std::optional;
|
||||||
using std::reference_wrapper;
|
using std::reference_wrapper;
|
||||||
|
|
||||||
optional<reference_wrapper<const TmxLayer>> layerGfx;
|
optional<reference_wrapper<const TmxLayer>> layerGfx, layerCls, layerPal;
|
||||||
optional<reference_wrapper<const TmxLayer>> layerCls;
|
|
||||||
optional<reference_wrapper<const TmxLayer>> layerPal;
|
|
||||||
optional<reference_wrapper<std::vector<const TmxObject>>> objGroups;
|
|
||||||
|
|
||||||
// Read layers
|
// Read layers
|
||||||
for (const auto& layer : map.Layers())
|
for (const auto& layer : map.Layers())
|
||||||
@@ -225,12 +34,6 @@ TmxReader::Error TmxReader::Open(const std::string& inPath,
|
|||||||
if (!layerGfx.has_value() && (graphicsName.empty() || name == graphicsName)) { layerGfx = layer; }
|
if (!layerGfx.has_value() && (graphicsName.empty() || name == graphicsName)) { layerGfx = layer; }
|
||||||
if (!collisionName.empty() && !layerCls.has_value() && name == collisionName) { layerCls = layer; }
|
if (!collisionName.empty() && !layerCls.has_value() && name == collisionName) { layerCls = layer; }
|
||||||
if (!paletteName.empty() && !layerPal.has_value() && name == paletteName) { layerPal = layer; }
|
if (!paletteName.empty() && !layerPal.has_value() && name == paletteName) { layerPal = layer; }
|
||||||
/*
|
|
||||||
else if (!objMapping.empty() && layer->getType() == tmx::Layer::Type::Object)
|
|
||||||
{
|
|
||||||
objGroups.emplace_back(layer->getLayerAs<ObjectGroup>());
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check layers
|
// Check layers
|
||||||
@@ -250,7 +53,7 @@ TmxReader::Error TmxReader::Open(const std::string& inPath,
|
|||||||
// Read graphics layer
|
// Read graphics layer
|
||||||
mGraphics.reserve(numTiles);
|
mGraphics.reserve(numTiles);
|
||||||
for (auto tmxTile : layerGfx.value().get().Tiles())
|
for (auto tmxTile : layerGfx.value().get().Tiles())
|
||||||
mGraphics.emplace_back(Tile{ tmxTile & ~FLIP_MASK, static_cast<uint8_t>((tmxTile & FLIP_MASK) >> 28) });
|
mGraphics.emplace_back(Tile{ tmxTile & ~TmxLayer::FLIP_MASK, static_cast<uint8_t>((tmxTile & TmxLayer::FLIP_MASK) >> 28) });
|
||||||
|
|
||||||
// Read optional layers
|
// Read optional layers
|
||||||
if (layerPal.has_value())
|
if (layerPal.has_value())
|
||||||
@@ -258,7 +61,7 @@ TmxReader::Error TmxReader::Open(const std::string& inPath,
|
|||||||
std::vector<uint32_t> v;
|
std::vector<uint32_t> v;
|
||||||
v.reserve(numTiles);
|
v.reserve(numTiles);
|
||||||
for (auto tmxTile : layerPal.value().get().Tiles())
|
for (auto tmxTile : layerPal.value().get().Tiles())
|
||||||
v.emplace_back(tmxTile & ~FLIP_MASK);
|
v.emplace_back(tmxTile & ~TmxLayer::FLIP_MASK);
|
||||||
mPalette.emplace(v);
|
mPalette.emplace(v);
|
||||||
}
|
}
|
||||||
if (layerCls.has_value())
|
if (layerCls.has_value())
|
||||||
@@ -266,56 +69,41 @@ TmxReader::Error TmxReader::Open(const std::string& inPath,
|
|||||||
std::vector<uint32_t> v;
|
std::vector<uint32_t> v;
|
||||||
v.reserve(numTiles);
|
v.reserve(numTiles);
|
||||||
for (auto tmxTile : layerCls.value().get().Tiles())
|
for (auto tmxTile : layerCls.value().get().Tiles())
|
||||||
v.emplace_back(tmxTile & ~FLIP_MASK);
|
v.emplace_back(tmxTile & ~TmxLayer::FLIP_MASK);
|
||||||
mCollision.emplace(v);
|
mCollision.emplace(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Read tilesets
|
// Read tilesets
|
||||||
const auto& tilesets = map.Tilesets();
|
const auto& tilesets = map.Tilesets();
|
||||||
mGidTable.reserve(tilesets.size());
|
mGidTable.reserve(tilesets.size());
|
||||||
for (const auto& set : tilesets)
|
std::ranges::transform(tilesets, std::back_inserter(mGidTable),
|
||||||
mGidTable.emplace_back(set.GidRange());
|
[](const auto& it) { return it.GidRange(); });
|
||||||
|
|
||||||
// Read objects
|
// Read objects
|
||||||
if (!objMapping.empty())
|
if (!map.ObjectGroups().empty())
|
||||||
{
|
{
|
||||||
std::vector<Object> v;
|
std::vector<Object> objs;
|
||||||
for (const auto& tmxObj : objGroups.value().get())
|
for (const auto& group : map.ObjectGroups())
|
||||||
{
|
{
|
||||||
auto it = objMapping.find(std::string(tmxObj.Name()));
|
const auto& tmxObjects = group.Objects();
|
||||||
if (it == objMapping.end())
|
objs.reserve(objs.size() + tmxObjects.size());
|
||||||
continue;
|
|
||||||
|
|
||||||
const auto& pos = tmxObj.Pos();
|
|
||||||
Object obj;
|
|
||||||
obj.id = it->second;
|
|
||||||
obj.x = pos.x;
|
|
||||||
obj.y = pos.y;
|
|
||||||
|
|
||||||
v.emplace_back(obj);
|
|
||||||
}
|
|
||||||
/*
|
|
||||||
for (const auto& group : objGroups)
|
|
||||||
{
|
|
||||||
const auto& tmxObjects = group.get().Objects();
|
|
||||||
v.reserve(v.size() + tmxObjects.size());
|
|
||||||
for (const auto& tmxObj : tmxObjects)
|
for (const auto& tmxObj : tmxObjects)
|
||||||
{
|
{
|
||||||
auto it = objMapping.find(tmxObj.getName());
|
auto it = objMapping.find(std::string(tmxObj.Name()));
|
||||||
if (it == objMapping.end())
|
if (it == objMapping.end())
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
const auto& aabb = tmxObj.getAABB();
|
const auto& aabb = tmxObj.Box();
|
||||||
Object obj;
|
objs.emplace_back(Object
|
||||||
obj.id = it->second;
|
{
|
||||||
obj.x = aabb.left;
|
.id = it->second,
|
||||||
obj.y = aabb.top;
|
.x = aabb.x,
|
||||||
|
.y = aabb.y
|
||||||
v.emplace_back(obj);
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
*/
|
if (!objs.empty())
|
||||||
mObjects.emplace(v);
|
mObjects.emplace(objs);
|
||||||
}
|
}
|
||||||
|
|
||||||
return Error::OK;
|
return Error::OK;
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
/* tmxreader.hpp - Copyright (C) 2015-2024 a dinosaur (zlib, see COPYING.txt) */
|
// SPDX-License-Identifier: Zlib
|
||||||
|
// SPDX-FileCopyrightText: (c) 2015-2024 a dinosaur
|
||||||
|
|
||||||
#ifndef TMXREADER_HPP
|
#ifndef TMXREADER_HPP
|
||||||
#define TMXREADER_HPP
|
#define TMXREADER_HPP
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
/* tmxtileset.hpp - Copyright (C) 2015-2024 a dinosaur (zlib, see COPYING.txt) */
|
// SPDX-License-Identifier: Zlib
|
||||||
|
// SPDX-FileCopyrightText: (c) 2015-2024 a dinosaur
|
||||||
|
|
||||||
#ifndef TMXTILESET_HPP
|
#ifndef TMXTILESET_HPP
|
||||||
#define TMXTILESET_HPP
|
#define TMXTILESET_HPP
|
||||||
@@ -10,15 +11,16 @@
|
|||||||
class TmxTileset
|
class TmxTileset
|
||||||
{
|
{
|
||||||
std::string mName, mSource;
|
std::string mName, mSource;
|
||||||
uint32_t mFirstGid = 0, mLastGid = 0;
|
uint32_t mFirstGid = 0, mTileCount = 0;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
TmxTileset(const std::string_view name, const std::string_view source, uint32_t firstGid, uint32_t lastGid)
|
TmxTileset(const std::string_view name, const std::string_view source, uint32_t firstGid, uint32_t tileCount)
|
||||||
: mName(name), mSource(source), mFirstGid(firstGid), mLastGid(lastGid) {}
|
: mName(name), mSource(source), mFirstGid(firstGid), mTileCount(tileCount) {}
|
||||||
|
|
||||||
[[nodiscard]] constexpr const std::string_view Name() const noexcept { return mName; }
|
[[nodiscard]] const std::string_view Name() const noexcept { return mName; }
|
||||||
[[nodiscard]] constexpr const std::string_view Source() const noexcept { return mSource; }
|
[[nodiscard]] const std::string_view Source() const noexcept { return mSource; }
|
||||||
[[nodiscard]] constexpr const std::pair<uint32_t, uint32_t> GidRange() const noexcept { return { mFirstGid, mLastGid }; }
|
[[nodiscard]] constexpr const std::pair<uint32_t, uint32_t> GidRange() const noexcept
|
||||||
|
{ return { mFirstGid, mFirstGid + mTileCount - 1 }; }
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif//TMXTILESET_HPP
|
#endif//TMXTILESET_HPP
|
||||||
|
|||||||
Reference in New Issue
Block a user