mirror of
https://github.com/ScrelliCopter/tmx2gba.git
synced 2025-02-21 03:29:25 +11:00
collect string handling utility functions into strtools
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
add_executable(tmx2gba
|
add_executable(tmx2gba
|
||||||
|
strtools.hpp strtools.cpp
|
||||||
argparse.hpp argparse.cpp
|
argparse.hpp argparse.cpp
|
||||||
$<$<NOT:$<TARGET_EXISTS:ZLIB::ZLIB>>:gzip.hpp gzip.cpp>
|
$<$<NOT:$<TARGET_EXISTS:ZLIB::ZLIB>>:gzip.hpp gzip.cpp>
|
||||||
tmxlayer.hpp
|
tmxlayer.hpp
|
||||||
|
|||||||
37
src/strtools.cpp
Normal file
37
src/strtools.cpp
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
// SPDX-License-Identifier: Zlib
|
||||||
|
// SPDX-FileCopyrightText: (c) 2015-2024 a dinosaur
|
||||||
|
|
||||||
|
#include "strtools.hpp"
|
||||||
|
#include <cctype>
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
|
||||||
|
const std::string_view TrimWhitespace(const std::string_view str)
|
||||||
|
{
|
||||||
|
auto beg = std::find_if_not(str.begin(), str.end(), ::isspace);
|
||||||
|
if (beg == std::end(str))
|
||||||
|
{
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
auto end = std::find_if_not(str.rbegin(), str.rend(), ::isspace);
|
||||||
|
auto begOff = std::distance(str.begin(), beg);
|
||||||
|
auto endOff = std::distance(end, str.rend()) - begOff;
|
||||||
|
using size_type = std::string::size_type;
|
||||||
|
return str.substr(static_cast<size_type>(begOff), static_cast<size_type>(endOff));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string SanitiseLabel(const std::string_view ident)
|
||||||
|
{
|
||||||
|
std::string out;
|
||||||
|
out.reserve(ident.length());
|
||||||
|
|
||||||
|
int last = '_';
|
||||||
|
for (int i : ident)
|
||||||
|
{
|
||||||
|
if (out.empty() && std::isdigit(i)) { continue; }
|
||||||
|
if (!std::isalnum(i)) { i = '_'; }
|
||||||
|
if (i != '_' || last != '_') { out.push_back(i); }
|
||||||
|
last = i;
|
||||||
|
}
|
||||||
|
return out;
|
||||||
|
}
|
||||||
112
src/strtools.hpp
Normal file
112
src/strtools.hpp
Normal file
@@ -0,0 +1,112 @@
|
|||||||
|
// SPDX-License-Identifier: Zlib
|
||||||
|
// SPDX-FileCopyrightText: (c) 2024 a dinosaur
|
||||||
|
|
||||||
|
#ifndef STRTOOLS_HPP
|
||||||
|
#define STRTOOLS_HPP
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <string_view>
|
||||||
|
|
||||||
|
// Cut leading & trailing whitespace (including newlines)
|
||||||
|
[[nodiscard]] const std::string_view TrimWhitespace(const std::string_view str);
|
||||||
|
|
||||||
|
// Convert string to valid C identifier
|
||||||
|
[[nodiscard]] std::string SanitiseLabel(const std::string_view ident);
|
||||||
|
|
||||||
|
|
||||||
|
#include <ostream>
|
||||||
|
|
||||||
|
// Template functions for converting unsigned ints to C/GNU style hex
|
||||||
|
|
||||||
|
static inline constexpr char CHexU(uint8_t h) { return "0123456789ABCDEF"[h >> 4]; }
|
||||||
|
static inline constexpr char CHexL(uint8_t l) { return "0123456789ABCDEF"[l & 15]; }
|
||||||
|
|
||||||
|
template <typename T> static void CHex(std::ostream& s, T x);
|
||||||
|
template <> void CHex(std::ostream& s, uint8_t x)
|
||||||
|
{
|
||||||
|
if (x > 9) s << "0x";
|
||||||
|
if (x > 15) s << CHexU(x);
|
||||||
|
s << CHexL(x);
|
||||||
|
}
|
||||||
|
template <> void CHex(std::ostream& s, uint16_t x)
|
||||||
|
{
|
||||||
|
if (x > 9) s << "0x";
|
||||||
|
if (x > 4095) s << CHexU(static_cast<uint8_t>(x >> 8));
|
||||||
|
if (x > 255) s << CHexL(static_cast<uint8_t>(x >> 8));
|
||||||
|
if (x > 15) s << CHexU(static_cast<uint8_t>(x));
|
||||||
|
s << CHexL(static_cast<uint8_t>(x));
|
||||||
|
}
|
||||||
|
template <> void CHex(std::ostream& s, uint32_t x)
|
||||||
|
{
|
||||||
|
if (x > 9) s << "0x";
|
||||||
|
if (x > 0xFFFFFFF) s << CHexU(static_cast<uint8_t>(x >> 24));
|
||||||
|
if (x > 0xFFFFFF) s << CHexL(static_cast<uint8_t>(x >> 24));
|
||||||
|
if (x > 0xFFFFF) s << CHexU(static_cast<uint8_t>(x >> 16));
|
||||||
|
if (x > 65535) s << CHexL(static_cast<uint8_t>(x >> 16));
|
||||||
|
if (x > 4095) s << CHexU(static_cast<uint8_t>(x >> 8));
|
||||||
|
if (x > 255) s << CHexL(static_cast<uint8_t>(x >> 8));
|
||||||
|
if (x > 15) s << CHexU(static_cast<uint8_t>(x));
|
||||||
|
s << CHexL(static_cast<uint8_t>(x));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
#include <limits>
|
||||||
|
#include <cstdlib>
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
// Templated string to int/float w/ exception-less error handling
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] static std::optional<T> IntFromStr(const char* str, int base = 0) noexcept
|
||||||
|
{
|
||||||
|
using std::numeric_limits;
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
char* end = nullptr;
|
||||||
|
long res = std::strtol(str, &end, base);
|
||||||
|
if (errno == ERANGE) { return std::nullopt; }
|
||||||
|
if (str == end) { return std::nullopt; }
|
||||||
|
if constexpr (sizeof(long) > sizeof(T))
|
||||||
|
{
|
||||||
|
if (res > numeric_limits<T>::max() || res < numeric_limits<T>::min())
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<T>(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] static std::optional<T> UintFromStr(const char* str, int base = 0) noexcept
|
||||||
|
{
|
||||||
|
using std::numeric_limits;
|
||||||
|
|
||||||
|
char* end = nullptr;
|
||||||
|
errno = 0;
|
||||||
|
unsigned long res = std::strtoul(str, &end, base);
|
||||||
|
if (errno == ERANGE) { return std::nullopt; }
|
||||||
|
if (str == end) { return std::nullopt; }
|
||||||
|
if constexpr (numeric_limits<unsigned long>::max() > numeric_limits<T>::max())
|
||||||
|
{
|
||||||
|
if (res > numeric_limits<T>::max()) { return std::nullopt; }
|
||||||
|
}
|
||||||
|
|
||||||
|
return static_cast<T>(res);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename T>
|
||||||
|
[[nodiscard]] static std::optional<T> FloatFromStr(const char* str) noexcept
|
||||||
|
{
|
||||||
|
char* end = nullptr;
|
||||||
|
T res;
|
||||||
|
errno = 0;
|
||||||
|
if constexpr (std::is_same_v<T, float>)
|
||||||
|
res = std::strtof(str, &end);
|
||||||
|
else
|
||||||
|
res = static_cast<T>(std::strtod(str, &end));
|
||||||
|
if (errno == ERANGE) { return std::nullopt; }
|
||||||
|
if (str == end) { return std::nullopt; }
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif//STRTOOLS_HPP
|
||||||
@@ -2,43 +2,11 @@
|
|||||||
// SPDX-FileCopyrightText: (c) 2024 a dinosaur
|
// SPDX-FileCopyrightText: (c) 2024 a dinosaur
|
||||||
|
|
||||||
#include "swriter.hpp"
|
#include "swriter.hpp"
|
||||||
|
#include "strtools.hpp"
|
||||||
#include <type_traits>
|
#include <type_traits>
|
||||||
#include <limits>
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
|
|
||||||
static inline constexpr char HexU(uint8_t h) { return "0123456789ABCDEF"[h >> 4]; }
|
|
||||||
static inline constexpr char HexL(uint8_t l) { return "0123456789ABCDEF"[l & 15]; }
|
|
||||||
|
|
||||||
template <typename T> static void CHex(std::ostream& s, T x);
|
|
||||||
template <> void CHex(std::ostream& s, uint8_t x)
|
|
||||||
{
|
|
||||||
if (x > 9) s << "0x";
|
|
||||||
if (x > 15) s << HexU(x);
|
|
||||||
s << HexL(x);
|
|
||||||
}
|
|
||||||
template <> void CHex(std::ostream& s, uint16_t x)
|
|
||||||
{
|
|
||||||
if (x > 9) s << "0x";
|
|
||||||
if (x > 4095) s << HexU(static_cast<uint8_t>(x >> 8));
|
|
||||||
if (x > 255) s << HexL(static_cast<uint8_t>(x >> 8));
|
|
||||||
if (x > 15) s << HexU(static_cast<uint8_t>(x));
|
|
||||||
s << HexL(static_cast<uint8_t>(x));
|
|
||||||
}
|
|
||||||
template <> void CHex(std::ostream& s, uint32_t x)
|
|
||||||
{
|
|
||||||
if (x > 9) s << "0x";
|
|
||||||
if (x > 0xFFFFFFF) s << HexU(static_cast<uint8_t>(x >> 24));
|
|
||||||
if (x > 0xFFFFFF) s << HexL(static_cast<uint8_t>(x >> 24));
|
|
||||||
if (x > 0xFFFFF) s << HexU(static_cast<uint8_t>(x >> 16));
|
|
||||||
if (x > 65535) s << HexL(static_cast<uint8_t>(x >> 16));
|
|
||||||
if (x > 4095) s << HexU(static_cast<uint8_t>(x >> 8));
|
|
||||||
if (x > 255) s << HexL(static_cast<uint8_t>(x >> 8));
|
|
||||||
if (x > 15) s << HexU(static_cast<uint8_t>(x));
|
|
||||||
s << HexL(static_cast<uint8_t>(x));
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
template <typename T> static constexpr const std::string_view DataType();
|
template <typename T> static constexpr const std::string_view DataType();
|
||||||
template <> constexpr const std::string_view DataType<uint8_t>() { return ".byte"; }
|
template <> constexpr const std::string_view DataType<uint8_t>() { return ".byte"; }
|
||||||
template <> constexpr const std::string_view DataType<uint16_t>() { return ".hword"; }
|
template <> constexpr const std::string_view DataType<uint16_t>() { return ".hword"; }
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ constexpr std::string_view copyrightStr("(c) 2015-2024 a dinosaur");
|
|||||||
#include "convert.hpp"
|
#include "convert.hpp"
|
||||||
#include "headerwriter.hpp"
|
#include "headerwriter.hpp"
|
||||||
#include "swriter.hpp"
|
#include "swriter.hpp"
|
||||||
|
#include "strtools.hpp"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <map>
|
#include <map>
|
||||||
@@ -115,26 +116,6 @@ static bool ParseArgs(int argc, char** argv, Arguments& params)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static std::string SanitiseLabel(const std::string_view ident)
|
|
||||||
{
|
|
||||||
std::string out;
|
|
||||||
out.reserve(ident.length());
|
|
||||||
|
|
||||||
int last = '_';
|
|
||||||
for (int i : ident)
|
|
||||||
{
|
|
||||||
if (out.empty() && std::isdigit(i))
|
|
||||||
continue;
|
|
||||||
if (!std::isalnum(i))
|
|
||||||
i = '_';
|
|
||||||
if (i != '_' || last != '_')
|
|
||||||
out.push_back(i);
|
|
||||||
last = i;
|
|
||||||
}
|
|
||||||
return out;
|
|
||||||
}
|
|
||||||
|
|
||||||
int main(int argc, char** argv)
|
int main(int argc, char** argv)
|
||||||
{
|
{
|
||||||
Arguments p;
|
Arguments p;
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
// SPDX-FileCopyrightText: (c) 2015-2024 a dinosaur
|
// SPDX-FileCopyrightText: (c) 2015-2024 a dinosaur
|
||||||
|
|
||||||
#include "tmxmap.hpp"
|
#include "tmxmap.hpp"
|
||||||
|
#include "strtools.hpp"
|
||||||
#include "config.h"
|
#include "config.h"
|
||||||
#include <pugixml.hpp>
|
#include <pugixml.hpp>
|
||||||
#include <base64.h>
|
#include <base64.h>
|
||||||
@@ -11,80 +12,10 @@
|
|||||||
# include "gzip.hpp"
|
# include "gzip.hpp"
|
||||||
#endif
|
#endif
|
||||||
#include <zstd.h>
|
#include <zstd.h>
|
||||||
#include <limits>
|
|
||||||
#include <cerrno>
|
#include <cerrno>
|
||||||
#include <optional>
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
[[nodiscard]] static std::optional<T> IntFromStr(const char* str, int base = 0) noexcept
|
|
||||||
{
|
|
||||||
using std::numeric_limits;
|
|
||||||
|
|
||||||
errno = 0;
|
|
||||||
char* end = nullptr;
|
|
||||||
long res = std::strtol(str, &end, base);
|
|
||||||
if (errno == ERANGE) { return std::nullopt; }
|
|
||||||
if (str == end) { return std::nullopt; }
|
|
||||||
if constexpr (sizeof(long) > sizeof(T))
|
|
||||||
{
|
|
||||||
if (res > numeric_limits<T>::max() || res < numeric_limits<T>::min())
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
|
|
||||||
return static_cast<T>(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
[[nodiscard]] static std::optional<T> UintFromStr(const char* str, int base = 0) noexcept
|
|
||||||
{
|
|
||||||
using std::numeric_limits;
|
|
||||||
|
|
||||||
char* end = nullptr;
|
|
||||||
errno = 0;
|
|
||||||
unsigned long res = std::strtoul(str, &end, base);
|
|
||||||
if (errno == ERANGE) { return std::nullopt; }
|
|
||||||
if (str == end) { return std::nullopt; }
|
|
||||||
if constexpr (numeric_limits<unsigned long>::max() > numeric_limits<T>::max())
|
|
||||||
{
|
|
||||||
if (res > numeric_limits<T>::max()) { return std::nullopt; }
|
|
||||||
}
|
|
||||||
|
|
||||||
return static_cast<T>(res);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename T>
|
|
||||||
[[nodiscard]] static std::optional<T> FloatFromStr(const char* str) noexcept
|
|
||||||
{
|
|
||||||
char* end = nullptr;
|
|
||||||
T res;
|
|
||||||
errno = 0;
|
|
||||||
if constexpr (std::is_same_v<T, float>)
|
|
||||||
res = std::strtof(str, &end);
|
|
||||||
else
|
|
||||||
res = static_cast<T>(std::strtod(str, &end));
|
|
||||||
if (errno == ERANGE) { return std::nullopt; }
|
|
||||||
if (str == end) { return std::nullopt; }
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
|
|
||||||
[[nodiscard]] static std::optional<std::string> UnBase64(const std::string_view base64)
|
|
||||||
{
|
|
||||||
// Cut leading & trailing whitespace (including newlines)
|
|
||||||
auto beg = std::find_if_not(base64.begin(), base64.end(), ::isspace);
|
|
||||||
if (beg == std::end(base64)) { return std::nullopt; }
|
|
||||||
auto end = std::find_if_not(base64.rbegin(), base64.rend(), ::isspace);
|
|
||||||
auto begOff = std::distance(base64.begin(), beg);
|
|
||||||
auto endOff = std::distance(end, base64.rend()) - begOff;
|
|
||||||
using size_type = std::string::size_type;
|
|
||||||
const auto trimmed = base64.substr(static_cast<size_type>(begOff), static_cast<size_type>(endOff));
|
|
||||||
|
|
||||||
// Decode base64 string
|
|
||||||
return base64_decode(trimmed);
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class Encoding { XML, BASE64, CSV, INVALID };
|
enum class Encoding { XML, BASE64, CSV, INVALID };
|
||||||
enum class Compression { NONE, GZIP, ZLIB, ZSTD, INVALID };
|
enum class Compression { NONE, GZIP, ZLIB, ZSTD, INVALID };
|
||||||
|
|
||||||
@@ -105,6 +36,7 @@ enum class Compression { NONE, GZIP, ZLIB, ZSTD, INVALID };
|
|||||||
return Compression::INVALID;
|
return Compression::INVALID;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
[[nodiscard]] static bool Decompress(Compression compression, std::span<uint32_t> out, const std::string_view decoded)
|
[[nodiscard]] static bool Decompress(Compression compression, std::span<uint32_t> out, const std::string_view decoded)
|
||||||
{
|
{
|
||||||
//FIXME: lmao what is big endian
|
//FIXME: lmao what is big endian
|
||||||
@@ -192,22 +124,22 @@ void TmxMap::ReadLayer(const pugi::xml_node& xNode)
|
|||||||
if (encoding == Encoding::BASE64)
|
if (encoding == Encoding::BASE64)
|
||||||
{
|
{
|
||||||
// Decode base64 string
|
// Decode base64 string
|
||||||
auto decoded = UnBase64(xData.child_value());
|
auto decoded = base64_decode(TrimWhitespace(xData.child_value()));
|
||||||
if (!decoded.has_value())
|
if (decoded.empty())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto compression = CompressionFromStr(xData.attribute("compression").value());
|
auto compression = CompressionFromStr(xData.attribute("compression").value());
|
||||||
if (compression == Compression::GZIP || compression == Compression::ZLIB || compression == Compression::ZSTD)
|
if (compression == Compression::GZIP || compression == Compression::ZLIB || compression == Compression::ZSTD)
|
||||||
{
|
{
|
||||||
tileDat.resize(numTiles);
|
tileDat.resize(numTiles);
|
||||||
if (!Decompress(compression, tileDat, decoded.value()))
|
if (!Decompress(compression, tileDat, decoded))
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (compression == Compression::NONE)
|
else if (compression == Compression::NONE)
|
||||||
{
|
{
|
||||||
tileDat.reserve(numTiles);
|
tileDat.reserve(numTiles);
|
||||||
const auto end = decoded.value().end();
|
const auto end = decoded.end();
|
||||||
for (auto it = decoded.value().begin(); it < end - 3;)
|
for (auto it = decoded.begin(); it < end - 3;)
|
||||||
{
|
{
|
||||||
uint32_t tile = static_cast<uint32_t>(static_cast<uint8_t>(*it++));
|
uint32_t tile = static_cast<uint32_t>(static_cast<uint8_t>(*it++));
|
||||||
tile |= static_cast<uint32_t>(static_cast<uint8_t>(*it++)) << 8u;
|
tile |= static_cast<uint32_t>(static_cast<uint8_t>(*it++)) << 8u;
|
||||||
|
|||||||
Reference in New Issue
Block a user