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

Initial version commit.

This commit is contained in:
2015-09-22 18:54:31 +10:00
parent 4d59c657d6
commit 169dbec1cd
14 changed files with 9154 additions and 0 deletions

212
src/XGetopt.cpp Normal file
View File

@@ -0,0 +1,212 @@
// 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
int optind = 0; // global argv index
int getopt(int argc, char *argv[], char *optstring)
{
static char *next = nullptr;
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;
}

123
src/base64.cpp Normal file
View File

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

348
src/tmx2gba.cpp Normal file
View File

@@ -0,0 +1,348 @@
/* tmx2gba.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 <iostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <cstdint>
#include <algorithm>
#include <rapidxml/rapidxml.hpp>
#include <base64.h>
#include <miniz.h>
#include <XGetopt.h>
static const std::string g_strUsage = "Usage: tmx2gba [-h] [-r offset] [-lc name] [-p 0-15] <-i inpath> <-o outpath>\nRun 'tmx2gba -h' to view all available options.";
static const std::string g_strFullHelp = R"(Usage: tmx2gba [-hr] [-p index] <-i inpath> <-o outpath>
-h ---------- Display this help & command info.
-l <name> --- Name of layer to use (default first layer in TMX).
-c <name> --- Output a separate 8bit collision map of the specified layer.
-r <offset> - Offset tile indices (default 0).
-p <0-15> --- Select which palette to use for 4-bit tilesets.
-i <path> --- Path to input TMX file.
-o <path> --- Path to output files.)";
struct STilemapInfo
{
uint32_t width;
uint32_t height;
};
bool DecodeMapData (
const std::string& a_strEncData,
int a_iWidth, int a_iHeight,
std::vector<uint8_t>* a_pvucOut
)
{
if ( a_pvucOut == nullptr )
{
return false;
}
// Decode base64 string.
unsigned int cutTheCrap = a_strEncData.find_first_not_of ( " \t\n\r" );
std::string strDec = base64_decode ( a_strEncData.substr ( cutTheCrap ) );
a_pvucOut->clear ();
a_pvucOut->resize ( a_iWidth * a_iHeight * 4 );
mz_ulong uiDstSize = a_pvucOut->size ();
// Decompress compressed data.
int iRes = uncompress (
a_pvucOut->data (), &uiDstSize,
(const uint8_t*)strDec.data (), strDec.size ()
);
strDec.clear ();
if ( iRes < 0 )
{
return false;
}
return true;
}
int main ( int argc, char** argv )
{
// Parse cmd line args.
std::string strInPath, strOutPath;
std::string strLayer, strCollisionlay;
int iOffset = 0;
int iPalette = 0;
char cOption;
while ( ( cOption = getopt ( argc, argv, "hr:l:c:p:i:o:" ) ) > 0 )
{
switch ( cOption )
{
case ( 'h' ):
std::cout << g_strFullHelp << std::endl;
return 0;
case ( 'l' ):
strLayer = optarg;
break;
case ( 'c' ):
strCollisionlay = optarg;
break;
case ( 'r' ):
iOffset = std::stoi ( optarg );
break;
case ( 'p' ):
iPalette = std::stoi ( optarg );
break;
case ( 'i' ):
strInPath = optarg;
break;
case ( 'o' ):
strOutPath = optarg;
break;
default:
break;
}
}
// Check my paranoia.
if ( strInPath.empty () )
{
std::cerr << "No input file specified." << std::endl;
std::cout << g_strUsage << std::endl;
return -1;
}
if ( strOutPath.empty () )
{
std::cerr << "No output file specified." << std::endl;
std::cout << g_strUsage << std::endl;
return -1;
}
if ( iPalette < 0 || iPalette > 15 )
{
std::cerr << "Invalid palette index." << std::endl;
std::cout << g_strUsage << std::endl;
return -1;
}
// Open & read input file.
std::ifstream fin ( strInPath );
if ( !fin.is_open () )
{
std::cerr << "Failed to open input file." << std::endl;
return -1;
}
std::stringstream buf;
buf << fin.rdbuf ();
fin.close ();
std::string strXml = buf.str ();
buf.clear ();
STilemapInfo info;
memset ( &info, 0, sizeof(STilemapInfo) );
std::string strEncData, strEncCollisionDat;
// Parse document.
rapidxml::xml_document<> xDoc;
xDoc.parse<0> ( (char*)strXml.c_str () );
// Get map node.
auto xMap = xDoc.first_node ( "map" );
if ( xMap != nullptr )
{
// Read map attribs.
rapidxml::xml_attribute<>* xAttrib = nullptr;
if ( ( xAttrib = xMap->first_attribute ( "width" ) ) != nullptr )
{
info.width = std::stoi ( xAttrib->value () );
}
if ( ( xAttrib = xMap->first_attribute ( "height" ) ) != nullptr )
{
info.height = std::stoi ( xAttrib->value () );
}
// Get layer node.
rapidxml::xml_node<>* xLayer = nullptr;
if ( strLayer.empty () )
{
xLayer = xMap->first_node ( "layer" );
}
else
{
// Find specified layer.
for ( auto xNode = xMap->first_node (); xNode != nullptr; xNode = xNode->next_sibling () )
{
// We only want layers.
if ( strcmp ( xNode->name (), "layer" ) == 0 )
{
// Use this layer if it matches our specified name.
auto xName = xNode->first_attribute ( "name" );
if ( xName != nullptr )
{
if ( strcmp ( xName->value (), strLayer.c_str () ) == 0 )
{
xLayer = xNode;
break;
}
}
}
}
}
// Find collision layer.
rapidxml::xml_node<>* xCollisionLay = nullptr;
if ( !strCollisionlay.empty () )
{
for ( auto xNode = xMap->first_node (); xNode != nullptr; xNode = xNode->next_sibling () )
{
if ( strcmp ( xNode->name (), "layer" ) == 0 )
{
// Use this layer if it matches our specified name.
auto xName = xNode->first_attribute ( "name" );
if ( xName != nullptr )
{
if ( strcmp ( xName->value (), strCollisionlay.c_str () ) == 0 )
{
xCollisionLay = xNode;
break;
}
}
}
}
}
// Read data from layer node.
if ( xLayer != nullptr )
{
auto xData = xLayer->first_node ( "data" );
if ( xData != nullptr )
{
// Get encoded data.
strEncData = xData->value ();
}
}
// Read data from collision layer.
if ( xCollisionLay != nullptr )
{
auto xData = xCollisionLay->first_node ( "data" );
if ( xData != nullptr )
{
// Get encoded data.
strEncCollisionDat = xData->value ();
}
}
}
// Check what we (don't) have.
if ( info.width == 0 || info.height == 0 || strEncData.empty () )
{
std::cerr << "Parse error.";
return -1;
}
// Decode and decompress layer data.
std::vector<uint8_t> vucLayerDat;
if ( !DecodeMapData ( strEncData, info.width, info.height, &vucLayerDat ) )
{
std::cerr << "Decompression error.";
return -1;
}
// Convert to GBA-friendly charmap data.
uint16_t* pRead = (uint16_t*)vucLayerDat.data ();
std::vector<uint16_t> vucCharDat;
vucCharDat.reserve ( info.width * info.height );
for ( size_t i = 0; i < size_t(info.width * info.height * 2); ++i )
{
uint16_t usTile = std::max<int> ( (*pRead++) + iOffset, 0 );
bool bFlipH = ( 0x8000 & *pRead ) ? true : false;
bool bFlipV = ( 0x4000 & *pRead++ ) ? true : false;
uint8_t ucFlags = 0x0;
ucFlags |= ( bFlipH ) ? 0x4 : 0x0;
ucFlags |= ( bFlipV ) ? 0x8 : 0x0;
ucFlags |= iPalette << 4;
vucCharDat.push_back ( usTile | ucFlags << 8 );
}
vucLayerDat.clear ();
// Save out charmap.
std::ofstream fout ( strOutPath, std::ios::binary );
if ( !fout.is_open () )
{
std::cerr << "Failed to create output file.";
return -1;
}
fout.write ( (const char*)vucCharDat.data (), vucCharDat.size () );
fout.close ();
// Decode & convert collision map.
std::vector<uint8_t> vucCollisionDat;
if ( !strEncCollisionDat.empty () )
{
std::vector<uint8_t> vucLayerDat;
if ( DecodeMapData ( strEncCollisionDat, info.width, info.height, &vucLayerDat ) )
{
vucCollisionDat.reserve ( info.width * info.height );
uint8_t* pRead = vucLayerDat.data ();
for ( size_t i = 0; i < info.width * info.height; ++i )
{
uint8_t ucTile = *pRead;
pRead += 4;
vucCollisionDat.push_back ( ucTile );
}
}
}
// Save it out or something like that.
if ( !vucCollisionDat.empty () )
{
// Try to nicely append "_collision" to the output name.
std::string strPath;
size_t extPos = strOutPath.find_last_of ( '.' );
if ( extPos != std::string::npos )
{
strPath = strOutPath.insert ( extPos, "_collision" );
}
else
{
strPath = strOutPath + "_collision";
}
// Save it out.
fout.open ( strPath, std::ios::binary );
if ( fout.is_open () )
{
fout.write ( (const char*)vucCollisionDat.data (), vucCollisionDat.size () );
fout.close ();
}
}
return 0;
}