1
0
mirror of https://github.com/ScrelliCopter/tmx2gba.git synced 2025-02-21 03:29:25 +11:00

13 Commits

41 changed files with 10109 additions and 6378 deletions

37
.github/workflows/cmake.yml vendored Normal file
View File

@@ -0,0 +1,37 @@
name: CMake
on:
push:
branches: [ "master" ]
pull_request:
branches: [ "master" ]
env:
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
BUILD_TYPE: Release
jobs:
build:
# The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac.
# You can convert this to a matrix build if you need cross-platform coverage.
# See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Configure CMake
# Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make.
# See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type
run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}}
- name: Build
# Build your program with the given configuration
run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}}
- name: Test
working-directory: ${{github.workspace}}/build
# Execute tests defined by the CMake configuration.
# See https://cmake.org/cmake/help/latest/manual/ctest.1.html for more detail
run: ctest -C ${{env.BUILD_TYPE}}

11
.gitignore vendored
View File

@@ -27,7 +27,7 @@
*.out *.out
*.app *.app
# VS rubbish. # VS rubbish
Debug/ Debug/
Release/ Release/
*.opensdf *.opensdf
@@ -35,7 +35,14 @@ Release/
*.v12.suo *.v12.suo
*.vcxproj.user *.vcxproj.user
# Some files I used for testing. # CLion Rubbish
.idea/
# CMake Rubbish
build/
cmake-build-*/
# Some files I used for testing
*.tmx *.tmx
*.raw *.raw
plains.png plains.png

View File

@@ -1,42 +1,38 @@
# tmx2gba - CMakeLists.txt cmake_minimum_required(VERSION "3.5" FATAL_ERROR)
# Copyright (C) 2018 Nicholas Curtis (a dinosaur) project(tmx2gba VERSION "0.3")
#
# 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.
cmake_minimum_required(VERSION 3.10) # Options
project(tmx2gba) option(TMX2GBA_DKP_INSTALL "Install into DEVKITPRO prefix" OFF)
option(ASAN "Enable address sanitiser" OFF)
# C++11 & C99
set(CMAKE_CXX_STANDARD 11) set(CMAKE_CXX_STANDARD 11)
set(CMAKE_C_STANDARD 99)
include_directories(inc) # Enable strong warnings
set(SOURCES if (MSVC)
src/tmx2gba.cpp string(REPLACE "/W3" "/W4" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}")
src/tmxlayer.cpp src/tmxlayer.h string(REPLACE "/W3" "/W4" CMAKE_C_FLAGS "${CMAKE_C_FLAGS}")
src/tmxobject.cpp src/tmxobject.h else()
src/tmxreader.cpp src/tmxreader.h add_compile_options(-Wall)
src/tmxtileset.cpp src/tmxtileset.h endif()
src/ultragetopt.c inc/ultragetopt.h if (ASAN)
src/base64.cpp inc/base64.h add_compile_options(-fsanitize=address -fno-omit-frame-pointer)
inc/miniz.h add_link_options(-fsanitize=address -shared-libasan)
endif()
inc/rapidxml/rapidxml.hpp # Libraries
inc/rapidxml/rapidxml_iterators.hpp add_subdirectory(ext/base64)
inc/rapidxml/rapidxml_print.hpp add_subdirectory(ext/miniz)
inc/rapidxml/rapidxml_utils.hpp) add_subdirectory(ext/rapidxml)
add_subdirectory(ext/ultragetopt)
add_executable(tmx2gba ${SOURCES}) # Main tmx2gba sources
add_subdirectory(src)
if (MSVC)
# Default to tmx2gba as startup project when generating Solutions
set_property(DIRECTORY ${CMAKE_SOURCE_DIR}
PROPERTY VS_STARTUP_PROJECT tmx2gba)
endif()

17
COPYING.txt Normal file
View File

@@ -0,0 +1,17 @@
Copyright (C) 2015-2022 a dinosaur
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.

View File

@@ -1,6 +1,6 @@
# tmx2gba # # tmx2gba #
tmx2gba is a simple command line utility that converts [Tiled](http://www.mapeditor.org/) .tmx maps to Game Boy Advance compatible charmaps. tmx2gba is a simple command line utility that converts [Tiled](http://www.mapeditor.org/) .tmx maps to Game Boy Advance formatted charmaps.
Originally developed for my own personal use, I've thrown it up on glorious Github/Gitlab/whatever in case this is of use to anyone else. Originally developed for my own personal use, I've thrown it up in case this is of use to anyone else.
If you find a bug, please open an issue. If you find a bug, please open an issue.
@@ -17,41 +17,53 @@ Enjoy!
tmx2gba [-h] [-r offset] [-lyc name] [-p 0-15] <-i inpath> <-o outpath> tmx2gba [-h] [-r offset] [-lyc name] [-p 0-15] <-i inpath> <-o outpath>
``` ```
Command | Required | Notes | Command | Required | Notes |
------------|----------|---------------------------------------------------------------------- |--------------|----------|-----------------------------------------------------------------------|
-h | N/A | Display help & command info. | -h | N/A | Display help & command info. |
-l (name) | No | Name of layer to use (default first layer in TMX). | -v | No | Display version & quit. |
-y (name) | No | Layer for palette mappings. | -l (name) | No | Name of layer to use (default first layer in TMX). |
-c (name) | No | Output a separate 8bit collision map of the specified layer. | -y (name) | No | Layer for palette mappings. |
-r (offset) | No | Offset tile indices (default 0). | -c (name) | No | Output a separate 8bit collision map of the specified layer. |
-p (0-15) | No | Select which palette to use for 4-bit tilesets. | -r (offset) | No | Offset tile indices (default 0). |
-m (name;id)| No | Map an object name to an ID, will enable object exports. | -p (0-15) | No | Select which palette to use for 4-bit tilesets. |
-i (path) | *Yes* | Path to input TMX file. | -m (name;id) | No | Map an object name to an ID, will enable object exports. |
-o (path) | *Yes* | Path to output files. | -i (path) | *Yes* | Path to input TMX file. |
-f <file> | No | Command line instructions list for easy integration with buildscripts | -o (path) | *Yes* | Path to output files. |
| -f <file> | No | Command line instructions list for easy integration with buildscripts |
### How do I build it? ### ### How do I build it? ###
Dependencies for building are CMake 3.x and a C++11 compliant compiler, Dependencies for building are CMake 3.x and a C++11 compliant compiler,
all other dependencies are in-tree so you should be able to build with: all other dependencies are in-tree so you should be able to build with:
```bash ```bash
mkdir build && cd build cmake -B build -DCMAKE_BUILD_TYPE=RelWithDebInfo
cmake .. make -C build -j$(nproc --all)
make
``` ```
Optionally, to make it convenient for my dkp projects: Optionally, you may install it to use it system wide:
```bash ```bash
sudo cp tmx2gba $DEVKITPRO/tools/bin/tmx2gba sudo cmake --install build
```
Which will copy the tmx2gba executable to /usr/local/bin/tmx2gba by default,
if you prefer to use /usr for some reason you may specify a prefix like so:
```bash
sudo cmake --install build --prefix /usr
```
If you're a devkitPro user and would prefer to keep all your development tools compartmentalised
you may optionally install to the tools directory with the `TMX2GBA_DKP_INSTALL` option (OFF by default).
The build scripts will respect your `DEVKITPRO` environment variable but if not set will install to
`/opt/devkitpro/tools/bin/tmx2gba` directly, the `--prefix` argument has no effect in this mode.
```bash
cmake -B build -DCMAKE_BUILD_TYPE=Release -DTMX2GBA_DKP_INSTALL:BOOL=ON
cmake --build build
sudo cmake --install build
``` ```
### Todo list ### ### Todo list ###
* Add support for multi-SBB prepared charmaps. * Add support for multi-SBB prepared charmaps.
* Test CMakeLists for Windows compatibility.
* Check if this works for NDS as well. * Check if this works for NDS as well.
* Compression support. * Compression support.
* Prehaps use GNU style getopt_long? * Support for less common TMX formats.
* Refactor & Fix bugs. *(duh)*
### License ### ### License ###
[tmx2gba](https://github.com/ScrelliCopter/tmx2gba) is licensed under the zlib license. [tmx2gba](https://github.com/ScrelliCopter/tmx2gba) is licensed under the zlib license.
@@ -61,7 +73,7 @@ sudo cp tmx2gba $DEVKITPRO/tools/bin/tmx2gba
[ultragetopt](https://github.com/kevinoid/ultragetopt) is licensed under the MIT license. [ultragetopt](https://github.com/kevinoid/ultragetopt) is licensed under the MIT license.
``` ```
Copyright (C) 2015-2019 Nicholas Curtis (a dinosaur) Copyright (C) 2015-2023 a dinosaur
This software is provided 'as-is', without any express or implied This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages warranty. In no event will the authors be held liable for any damages

View File

@@ -0,0 +1,5 @@
add_library(base64
base64.cpp base64.h)
add_library(External::base64 ALIAS base64)
target_include_directories(base64
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

282
ext/base64/base64.cpp Normal file
View File

@@ -0,0 +1,282 @@
/*
base64.cpp and base64.h
base64 encoding and decoding with C++.
More information at
https://renenyffenegger.ch/notes/development/Base64/Encoding-and-decoding-base-64-with-cpp
Version: 2.rc.08 (release candidate)
Copyright (C) 2004-2017, 2020, 2021 René Nyffenegger
This source code is provided 'as-is', without any express or implied
warranty. In no event will the author 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 source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
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 source code.
3. This notice may not be removed or altered from any source distribution.
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/
#include "base64.h"
#include <algorithm>
#include <stdexcept>
//
// Depending on the url parameter in base64_chars, one of
// two sets of base64 characters needs to be chosen.
// They differ in their last two characters.
//
static const char* base64_chars[2] = {
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789"
"+/",
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789"
"-_"};
static unsigned int pos_of_char(const unsigned char chr) {
//
// Return the position of chr within base64_encode()
//
if (chr >= 'A' && chr <= 'Z') return chr - 'A';
else if (chr >= 'a' && chr <= 'z') return chr - 'a' + ('Z' - 'A') + 1;
else if (chr >= '0' && chr <= '9') return chr - '0' + ('Z' - 'A') + ('z' - 'a') + 2;
else if (chr == '+' || chr == '-') return 62; // Be liberal with input and accept both url ('-') and non-url ('+') base 64 characters (
else if (chr == '/' || chr == '_') return 63; // Ditto for '/' and '_'
else
//
// 2020-10-23: Throw std::exception rather than const char*
//(Pablo Martin-Gomez, https://github.com/Bouska)
//
throw std::runtime_error("Input is not valid base64-encoded data.");
}
static std::string insert_linebreaks(std::string str, size_t distance) {
//
// Provided by https://github.com/JomaCorpFX, adapted by me.
//
if (!str.length()) {
return "";
}
size_t pos = distance;
while (pos < str.size()) {
str.insert(pos, "\n");
pos += distance + 1;
}
return str;
}
template <typename String, unsigned int line_length>
static std::string encode_with_line_breaks(String s) {
return insert_linebreaks(base64_encode(s, false), line_length);
}
template <typename String>
static std::string encode_pem(String s) {
return encode_with_line_breaks<String, 64>(s);
}
template <typename String>
static std::string encode_mime(String s) {
return encode_with_line_breaks<String, 76>(s);
}
template <typename String>
static std::string encode(String s, bool url) {
return base64_encode(reinterpret_cast<const unsigned char*>(s.data()), s.length(), url);
}
std::string base64_encode(unsigned char const* bytes_to_encode, size_t in_len, bool url) {
size_t len_encoded = (in_len +2) / 3 * 4;
unsigned char trailing_char = url ? '.' : '=';
//
// Choose set of base64 characters. They differ
// for the last two positions, depending on the url
// parameter.
// A bool (as is the parameter url) is guaranteed
// to evaluate to either 0 or 1 in C++ therefore,
// the correct character set is chosen by subscripting
// base64_chars with url.
//
const char* base64_chars_ = base64_chars[url];
std::string ret;
ret.reserve(len_encoded);
unsigned int pos = 0;
while (pos < in_len) {
ret.push_back(base64_chars_[(bytes_to_encode[pos + 0] & 0xfc) >> 2]);
if (pos+1 < in_len) {
ret.push_back(base64_chars_[((bytes_to_encode[pos + 0] & 0x03) << 4) + ((bytes_to_encode[pos + 1] & 0xf0) >> 4)]);
if (pos+2 < in_len) {
ret.push_back(base64_chars_[((bytes_to_encode[pos + 1] & 0x0f) << 2) + ((bytes_to_encode[pos + 2] & 0xc0) >> 6)]);
ret.push_back(base64_chars_[ bytes_to_encode[pos + 2] & 0x3f]);
}
else {
ret.push_back(base64_chars_[(bytes_to_encode[pos + 1] & 0x0f) << 2]);
ret.push_back(trailing_char);
}
}
else {
ret.push_back(base64_chars_[(bytes_to_encode[pos + 0] & 0x03) << 4]);
ret.push_back(trailing_char);
ret.push_back(trailing_char);
}
pos += 3;
}
return ret;
}
template <typename String>
static std::string decode(String encoded_string, bool remove_linebreaks) {
//
// decode(…) is templated so that it can be used with String = const std::string&
// or std::string_view (requires at least C++17)
//
if (encoded_string.empty()) return std::string();
if (remove_linebreaks) {
std::string copy(encoded_string);
copy.erase(std::remove(copy.begin(), copy.end(), '\n'), copy.end());
return base64_decode(copy, false);
}
size_t length_of_string = encoded_string.length();
size_t pos = 0;
//
// The approximate length (bytes) of the decoded string might be one or
// two bytes smaller, depending on the amount of trailing equal signs
// in the encoded string. This approximation is needed to reserve
// enough space in the string to be returned.
//
size_t approx_length_of_decoded_string = length_of_string / 4 * 3;
std::string ret;
ret.reserve(approx_length_of_decoded_string);
while (pos < length_of_string) {
//
// Iterate over encoded input string in chunks. The size of all
// chunks except the last one is 4 bytes.
//
// The last chunk might be padded with equal signs or dots
// in order to make it 4 bytes in size as well, but this
// is not required as per RFC 2045.
//
// All chunks except the last one produce three output bytes.
//
// The last chunk produces at least one and up to three bytes.
//
size_t pos_of_char_1 = pos_of_char(encoded_string[pos+1] );
//
// Emit the first output byte that is produced in each chunk:
//
ret.push_back(static_cast<std::string::value_type>( ( (pos_of_char(encoded_string[pos+0]) ) << 2 ) + ( (pos_of_char_1 & 0x30 ) >> 4)));
if ( ( pos + 2 < length_of_string ) && // Check for data that is not padded with equal signs (which is allowed by RFC 2045)
encoded_string[pos+2] != '=' &&
encoded_string[pos+2] != '.' // accept URL-safe base 64 strings, too, so check for '.' also.
)
{
//
// Emit a chunk's second byte (which might not be produced in the last chunk).
//
unsigned int pos_of_char_2 = pos_of_char(encoded_string[pos+2] );
ret.push_back(static_cast<std::string::value_type>( (( pos_of_char_1 & 0x0f) << 4) + (( pos_of_char_2 & 0x3c) >> 2)));
if ( ( pos + 3 < length_of_string ) &&
encoded_string[pos+3] != '=' &&
encoded_string[pos+3] != '.'
)
{
//
// Emit a chunk's third byte (which might not be produced in the last chunk).
//
ret.push_back(static_cast<std::string::value_type>( ( (pos_of_char_2 & 0x03 ) << 6 ) + pos_of_char(encoded_string[pos+3]) ));
}
}
pos += 4;
}
return ret;
}
std::string base64_decode(std::string const& s, bool remove_linebreaks) {
return decode(s, remove_linebreaks);
}
std::string base64_encode(std::string const& s, bool url) {
return encode(s, url);
}
std::string base64_encode_pem (std::string const& s) {
return encode_pem(s);
}
std::string base64_encode_mime(std::string const& s) {
return encode_mime(s);
}
#if __cplusplus >= 201703L
//
// Interface with std::string_view rather than const std::string&
// Requires C++17
// Provided by Yannic Bonenberger (https://github.com/Yannic)
//
std::string base64_encode(std::string_view s, bool url) {
return encode(s, url);
}
std::string base64_encode_pem(std::string_view s) {
return encode_pem(s);
}
std::string base64_encode_mime(std::string_view s) {
return encode_mime(s);
}
std::string base64_decode(std::string_view s, bool remove_linebreaks) {
return decode(s, remove_linebreaks);
}
#endif // __cplusplus >= 201703L

35
ext/base64/base64.h Normal file
View File

@@ -0,0 +1,35 @@
//
// base64 encoding and decoding with C++.
// Version: 2.rc.08 (release candidate)
//
#ifndef BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A
#define BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A
#include <string>
#if __cplusplus >= 201703L
#include <string_view>
#endif // __cplusplus >= 201703L
std::string base64_encode (std::string const& s, bool url = false);
std::string base64_encode_pem (std::string const& s);
std::string base64_encode_mime(std::string const& s);
std::string base64_decode(std::string const& s, bool remove_linebreaks = false);
std::string base64_encode(unsigned char const*, size_t len, bool url = false);
#if __cplusplus >= 201703L
//
// Interface with std::string_view rather than const std::string&
// Requires C++17
// Provided by Yannic Bonenberger (https://github.com/Yannic)
//
std::string base64_encode (std::string_view s, bool url = false);
std::string base64_encode_pem (std::string_view s);
std::string base64_encode_mime(std::string_view s);
std::string base64_decode(std::string_view s, bool remove_linebreaks = false);
#endif // __cplusplus >= 201703L
#endif /* BASE64_H_C0CE2A47_D10E_42C9_A27C_C883944E704A */

5
ext/miniz/CMakeLists.txt Normal file
View File

@@ -0,0 +1,5 @@
add_library(miniz
miniz.c miniz.h)
add_library(External::miniz ALIAS miniz)
target_include_directories(miniz
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})

7733
ext/miniz/miniz.c Normal file

File diff suppressed because it is too large Load Diff

1350
ext/miniz/miniz.h Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,4 @@
add_library(rapidxml INTERFACE)
add_library(External::rapidxml ALIAS rapidxml)
target_include_directories(rapidxml
INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})

View File

@@ -0,0 +1,22 @@
include(CheckIncludeFile)
include(CheckFunctionExists)
check_include_file(strings.h HAVE_STRINGS_H)
check_function_exists(strcasecmp HAVE_STRCASECMP)
check_function_exists(_stricmp HAVE__STRICMP)
check_function_exists(strncasecmp HAVE_STRNCASECMP)
check_function_exists(_strnicmp HAVE__STRNICMP)
add_library(ultragetopt
ultragetopt.c ultragetopt.h)
add_library(External::ultragetopt ALIAS ultragetopt)
target_include_directories(ultragetopt
PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
target_compile_definitions(ultragetopt PRIVATE
$<$<BOOL:${HAVE_STRINGS_H}>:HAVE_STRINGS_H=1>
$<$<BOOL:${HAVE_STRCASECMP}>:HAVE_STRCASECMP=1>
$<$<BOOL:${HAVE__STRICMP}>:HAVE__STRICMP=1>
$<$<BOOL:${HAVE_STRNCASECMP}>:HAVE_STRNCASECMP=1>
$<$<BOOL:${HAVE__STRNICMP}>:HAVE__STRNICMP=1>)

View File

@@ -1,9 +0,0 @@
#ifndef __BASE64_H__
#define __BASE64_H__
#include <string>
std::string base64_encode(unsigned char const* , unsigned int len);
std::string base64_decode(std::string const& s);
#endif

File diff suppressed because it is too large Load Diff

22
src/CMakeLists.txt Normal file
View File

@@ -0,0 +1,22 @@
add_executable(tmx2gba
tmx2gba.cpp
tmxlayer.hpp
tmxobject.hpp
tmxreader.hpp tmxreader.cpp
tmxtileset.hpp)
target_link_libraries(tmx2gba
External::base64
External::miniz
External::rapidxml
External::ultragetopt)
if (TMX2GBA_DKP_INSTALL)
if (DEFINED ENV{DEVKITPRO})
set(TMX2GBA_INSTALL_DESTINATION "$ENV{DEVKITPRO}/tools/bin")
else()
set(TMX2GBA_INSTALL_DESTINATION /opt/devkitpro/tools/bin)
endif()
else()
set(TMX2GBA_INSTALL_DESTINATION bin)
endif()
install(TARGETS tmx2gba RUNTIME DESTINATION "${TMX2GBA_INSTALL_DESTINATION}")

View File

@@ -1,212 +0,0 @@
// XGetopt.cpp Version 1.2
//
// Author: Hans Dietrich
// hdietrich2@hotmail.com
//
// Description:
// XGetopt.cpp implements getopt(), a function to parse command lines.
//
// History
// Version 1.2 - 2003 May 17
// - Added Unicode support
//
// Version 1.1 - 2002 March 10
// - Added example to XGetopt.cpp module header
//
// This software is released into the public domain.
// You are free to use it in any way you like.
//
// This software is provided "as is" with no expressed
// or implied warranty. I accept no liability for any
// damage or loss of business that this software may cause.
//
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
// if you are not using precompiled headers then include these lines:
#include <stdio.h>
#include <string.h>
///////////////////////////////////////////////////////////////////////////////
#include "XGetopt.h"
///////////////////////////////////////////////////////////////////////////////
//
// X G e t o p t . c p p
//
//
// NAME
// getopt -- parse command line options
//
// SYNOPSIS
// int getopt(int argc, char *argv[], char *optstring)
//
// extern char *optarg;
// extern int optind;
//
// DESCRIPTION
// The getopt() function parses the command line arguments. Its
// arguments argc and argv are the argument count and array as
// passed into the application on program invocation. In the case
// of Visual C++ programs, argc and argv are available via the
// variables __argc and __argv (double underscores), respectively.
// getopt returns the next option letter in argv that matches a
// letter in optstring. (Note: Unicode programs should use
// __targv instead of __argv. Also, all character and string
// literals should be enclosed in _T( ) ).
//
// optstring is a string of recognized option letters; if a letter
// is followed by a colon, the option is expected to have an argument
// that may or may not be separated from it by white space. optarg
// is set to point to the start of the option argument on return from
// getopt.
//
// Option letters may be combined, e.g., "-ab" is equivalent to
// "-a -b". Option letters are case sensitive.
//
// getopt places in the external variable optind the argv index
// of the next argument to be processed. optind is initialized
// to 0 before the first call to getopt.
//
// When all options have been processed (i.e., up to the first
// non-option argument), getopt returns EOF, optarg will point
// to the argument, and optind will be set to the argv index of
// the argument. If there are no non-option arguments, optarg
// will be set to nullptr.
//
// The special option "--" may be used to delimit the end of the
// options; EOF will be returned, and "--" (and everything after it)
// will be skipped.
//
// RETURN VALUE
// For option letters contained in the string optstring, getopt
// will return the option letter. getopt returns a question mark (?)
// when it encounters an option letter not included in optstring.
// EOF is returned when processing is finished.
//
// BUGS
// 1) Long options are not supported.
// 2) The GNU double-colon extension is not supported.
// 3) The environment variable POSIXLY_CORRECT is not supported.
// 4) The + syntax is not supported.
// 5) The automatic permutation of arguments is not supported.
// 6) This implementation of getopt() returns EOF if an error is
// encountered, instead of -1 as the latest standard requires.
//
// EXAMPLE
// BOOL CMyApp::ProcessCommandLine(int argc, char *argv[])
// {
// int c;
//
// while ((c = getopt(argc, argv, _T("aBn:"))) != EOF)
// {
// switch (c)
// {
// case _T('a'):
// TRACE(_T("option a\n"));
// //
// // set some flag here
// //
// break;
//
// case _T('B'):
// TRACE( _T("option B\n"));
// //
// // set some other flag here
// //
// break;
//
// case _T('n'):
// TRACE(_T("option n: value=%d\n"), atoi(optarg));
// //
// // do something with value here
// //
// break;
//
// case _T('?'):
// TRACE(_T("ERROR: illegal option %s\n"), argv[optind-1]);
// return FALSE;
// break;
//
// default:
// TRACE(_T("WARNING: no handler for option %c\n"), c);
// return FALSE;
// break;
// }
// }
// //
// // check for non-option args here
// //
// return TRUE;
// }
//
///////////////////////////////////////////////////////////////////////////////
char *optarg; // global argument pointer
char *next;
int optind = 0; // global argv index
int getopt(int argc, char *argv[], char *optstring)
{
if (optind == 0)
next = nullptr;
optarg = nullptr;
if (next == nullptr || *next == '\0')
{
if (optind == 0)
optind++;
if (optind >= argc || argv[optind][0] != '-' || argv[optind][1] == '\0')
{
optarg = nullptr;
if (optind < argc)
optarg = argv[optind];
return EOF;
}
if (strcmp(argv[optind], "--") == 0)
{
optind++;
optarg = nullptr;
if (optind < argc)
optarg = argv[optind];
return EOF;
}
next = argv[optind];
next++; // skip past -
optind++;
}
char c = *next++;
char *cp = strchr(optstring, c);
if (cp == nullptr || c == ':')
return '?';
cp++;
if (*cp == ':')
{
if (*next != '\0')
{
optarg = next;
next = nullptr;
}
else if (optind < argc)
{
optarg = argv[optind];
optind++;
}
else
{
return '?';
}
}
return c;
}

View File

@@ -1,123 +0,0 @@
/*
base64.cpp and base64.h
Copyright (C) 2004-2008 René Nyffenegger
This source code is provided 'as-is', without any express or implied
warranty. In no event will the author 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 source code must not be misrepresented; you must not
claim that you wrote the original source code. If you use this source code
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 source code.
3. This notice may not be removed or altered from any source distribution.
René Nyffenegger rene.nyffenegger@adp-gmbh.ch
*/
#include "base64.h"
#include <iostream>
static const std::string base64_chars =
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz"
"0123456789+/";
static inline bool is_base64(unsigned char c) {
return (isalnum(c) || (c == '+') || (c == '/'));
}
std::string base64_encode(unsigned char const* bytes_to_encode, unsigned int in_len) {
std::string ret;
int i = 0;
int j = 0;
unsigned char char_array_3[3];
unsigned char char_array_4[4];
while (in_len--) {
char_array_3[i++] = *(bytes_to_encode++);
if (i == 3) {
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for(i = 0; (i <4) ; i++)
ret += base64_chars[char_array_4[i]];
i = 0;
}
}
if (i)
{
for(j = i; j < 3; j++)
char_array_3[j] = '\0';
char_array_4[0] = (char_array_3[0] & 0xfc) >> 2;
char_array_4[1] = ((char_array_3[0] & 0x03) << 4) + ((char_array_3[1] & 0xf0) >> 4);
char_array_4[2] = ((char_array_3[1] & 0x0f) << 2) + ((char_array_3[2] & 0xc0) >> 6);
char_array_4[3] = char_array_3[2] & 0x3f;
for (j = 0; (j < i + 1); j++)
ret += base64_chars[char_array_4[j]];
while((i++ < 3))
ret += '=';
}
return ret;
}
std::string base64_decode(std::string const& encoded_string) {
int in_len = encoded_string.size();
int i = 0;
int j = 0;
int in_ = 0;
unsigned char char_array_4[4], char_array_3[3];
std::string ret;
while (in_len-- && ( encoded_string[in_] != '=') && is_base64(encoded_string[in_])) {
char_array_4[i++] = encoded_string[in_]; in_++;
if (i ==4) {
for (i = 0; i <4; i++)
char_array_4[i] = base64_chars.find(char_array_4[i]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (i = 0; (i < 3); i++)
ret += char_array_3[i];
i = 0;
}
}
if (i) {
for (j = i; j <4; j++)
char_array_4[j] = 0;
for (j = 0; j <4; j++)
char_array_4[j] = base64_chars.find(char_array_4[j]);
char_array_3[0] = (char_array_4[0] << 2) + ((char_array_4[1] & 0x30) >> 4);
char_array_3[1] = ((char_array_4[1] & 0xf) << 4) + ((char_array_4[2] & 0x3c) >> 2);
char_array_3[2] = ((char_array_4[2] & 0x3) << 6) + char_array_4[3];
for (j = 0; (j < i - 1); j++) ret += char_array_3[j];
}
return ret;
}

View File

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

View File

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

View File

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

34
src/tmxlayer.hpp Normal file
View File

@@ -0,0 +1,34 @@
/* tmxlayer.hpp - Copyright (C) 2015-2022 a dinosaur (zlib, see COPYING.txt) */
#ifndef TMXLAYER_HPP
#define TMXLAYER_HPP
#include <string>
#include <cstdint>
#include <utility>
class TmxLayer
{
public:
static constexpr uint32_t FLIP_HORZ = 0x80000000;
static constexpr uint32_t FLIP_VERT = 0x40000000;
static constexpr uint32_t FLIP_DIAG = 0x20000000;
static constexpr uint32_t FLIP_MASK = 0xE0000000;
TmxLayer() : mWidth(0), mHeight(0), mTileDat(nullptr) {}
TmxLayer(int aWidth, int aHeight, std::string aName, uint32_t* aTileDat)
: mName(std::move(aName)), mWidth(aWidth), mHeight(aHeight), mTileDat(aTileDat) {}
inline ~TmxLayer() { delete[] mTileDat; }
constexpr const std::string& GetName() const { return mName; }
constexpr int GetWidth() const { return mWidth; }
constexpr int GetHeight() const { return mHeight; }
constexpr const uint32_t* GetData() const { return mTileDat; }
private:
std::string mName;
int mWidth, mHeight;
uint32_t* mTileDat;
};
#endif//TMXLAYER_HPP

View File

@@ -1,61 +0,0 @@
/* tmxobject.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 "tmxobject.h"
CTmxObject::CTmxObject () :
m_name ( "" ),
m_x ( 0.0f ), m_y ( 0.0f )
{
}
CTmxObject::CTmxObject ( const std::string& a_name, float a_x, float a_y ) :
m_name ( a_name ),
m_x ( a_x ), m_y ( a_y )
{
}
CTmxObject::~CTmxObject ()
{
}
const std::string& CTmxObject::GetName () const
{
return m_name;
}
void CTmxObject::GetPos ( float* a_outX, float* a_outY ) const
{
if ( a_outX != nullptr )
{
*a_outX = m_x;
}
if ( a_outY != nullptr )
{
*a_outY = m_y;
}
}

View File

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

25
src/tmxobject.hpp Normal file
View File

@@ -0,0 +1,25 @@
/* tmxobject.hpp - Copyright (C) 2015-2022 a dinosaur (zlib, see COPYING.txt) */
#ifndef TMXOBJECT_HPP
#define TMXOBJECT_HPP
#include <string>
#include <utility>
class TmxObject
{
public:
TmxObject() : mX(0.0f), mY(0.0f) {}
TmxObject(std::string aName, float aX, float aY)
: mName(std::move(aName)), mX(aX), mY(aY) {}
~TmxObject() = default;
constexpr const std::string& GetName() const { return mName; }
inline void GetPos(float& aOutX, float& aOutY) const { aOutX = mX; aOutY = mY; }
private:
std::string mName;
float mX, mY;
};
#endif//TMXOBJECT_HPP

View File

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

View File

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

50
src/tmxreader.hpp Normal file
View File

@@ -0,0 +1,50 @@
/* tmxreader.hpp - Copyright (C) 2015-2022 a dinosaur (zlib, see COPYING.txt) */
#ifndef TMXREADER_HPP
#define TMXREADER_HPP
#include <istream>
#include <vector>
#include <string>
#include <cstdint>
#include <rapidxml/rapidxml.hpp>
class TmxTileset;
class TmxLayer;
class TmxObject;
class TmxReader
{
public:
TmxReader() = default;
~TmxReader();
void Open(std::istream& aIn);
constexpr int GetWidth() const { return mWidth; }
constexpr int GetHeight() const { return mHeight; }
inline const TmxLayer* GetLayer(size_t aId) const { return mLayers.at(aId); }
const TmxLayer* GetLayer(const std::string& aName) const;
inline std::size_t GetLayerCount() const { return mLayers.size(); }
inline const TmxObject* GetObject(size_t aId) const { return mObjects.at(aId); }
inline size_t GetObjectCount() const { return mObjects.size(); }
uint32_t LidFromGid(uint32_t aGid);
private:
static bool DecodeMap(uint32_t* aOut, size_t aOutSize, const std::string& aBase64Dat);
void ReadTileset(rapidxml::xml_node<>* aXNode);
void ReadLayer(rapidxml::xml_node<>* aXNode);
void ReadObjects(rapidxml::xml_node<>* aXNode);
int mWidth, mHeight;
std::vector<TmxTileset*> mTilesets;
std::vector<TmxLayer*> mLayers;
std::vector<TmxObject*> mObjects;
std::vector<uint32_t> mGidTable;
};
#endif//TMXREADER_HPP

View File

@@ -1,60 +0,0 @@
/* 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;
}

View File

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

29
src/tmxtileset.hpp Normal file
View File

@@ -0,0 +1,29 @@
/* tmxtileset.hpp - Copyright (C) 2015-2022 a dinosaur (zlib, see COPYING.txt) */
#ifndef TMXTILESET_HPP
#define TMXTILESET_HPP
#include <string>
#include <cstdint>
#include <utility>
class TmxTileset
{
public:
TmxTileset() : mFirstGid(0) {}
TmxTileset(std::string aName, std::string aSource, uint32_t aFirstGid)
: mName(std::move(aName)), mSource(std::move(aSource)), mFirstGid(aFirstGid) {}
~TmxTileset() = default;
constexpr const std::string& GetName() const { return mName; }
constexpr const std::string& GetSource() const { return mSource; }
constexpr uint32_t GetFirstGid() const { return mFirstGid; }
private:
std::string mName;
std::string mSource;
uint32_t mFirstGid;
};
#endif//TMXTILESET_HPP

View File

@@ -1,22 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2013
VisualStudioVersion = 12.0.40629.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "tmx2gba", "tmx2gba.vcxproj", "{F8167A3B-4B77-4449-B730-F19ECB133A08}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
Release|Win32 = Release|Win32
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{F8167A3B-4B77-4449-B730-F19ECB133A08}.Debug|Win32.ActiveCfg = Debug|Win32
{F8167A3B-4B77-4449-B730-F19ECB133A08}.Debug|Win32.Build.0 = Debug|Win32
{F8167A3B-4B77-4449-B730-F19ECB133A08}.Release|Win32.ActiveCfg = Release|Win32
{F8167A3B-4B77-4449-B730-F19ECB133A08}.Release|Win32.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

View File

@@ -1,93 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{F8167A3B-4B77-4449-B730-F19ECB133A08}</ProjectGuid>
<RootNamespace>tmx2gba</RootNamespace>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v140</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>MultiByte</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<PropertyGroup />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<Optimization>Disabled</Optimization>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>inc\</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level4</WarningLevel>
<Optimization>MaxSpeed</Optimization>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>inc\</AdditionalIncludeDirectories>
</ClCompile>
<Link>
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="src\base64.cpp" />
<ClCompile Include="src\tmx2gba.cpp" />
<ClCompile Include="src\tmxlayer.cpp" />
<ClCompile Include="src\tmxobject.cpp" />
<ClCompile Include="src\tmxreader.cpp" />
<ClCompile Include="src\XGetopt.cpp" />
<ClCompile Include="src\tmxtileset.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="inc\base64.h" />
<ClInclude Include="inc\miniz.h" />
<ClInclude Include="inc\rapidxml\rapidxml.hpp" />
<ClInclude Include="inc\rapidxml\rapidxml_iterators.hpp" />
<ClInclude Include="inc\rapidxml\rapidxml_print.hpp" />
<ClInclude Include="inc\rapidxml\rapidxml_utils.hpp" />
<ClInclude Include="inc\XGetopt.h" />
<ClInclude Include="src\tmxlayer.h" />
<ClInclude Include="src\tmxobject.h" />
<ClInclude Include="src\tmxreader.h" />
<ClInclude Include="src\tmxtileset.h" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

View File

@@ -1,75 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="src\tmx2gba.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\base64.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\XGetopt.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tmxreader.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tmxlayer.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tmxtileset.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="src\tmxobject.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="inc\base64.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="inc\rapidxml\rapidxml.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="inc\rapidxml\rapidxml_iterators.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="inc\rapidxml\rapidxml_print.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="inc\rapidxml\rapidxml_utils.hpp">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="inc\miniz.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="inc\XGetopt.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\tmxreader.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\tmxlayer.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\tmxtileset.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="src\tmxobject.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
</Project>