/// /// T I L E S O N V E R S I O N 1 . 3 . 0 /// ------------------------------------------------ /// BSD 2-Clause License /// /// Copyright (c) 2020, Robin Berg Pettersen /// All rights reserved. /// /// Redistribution and use in source and binary forms, with or without /// modification, are permitted provided that the following conditions are met: /// /// 1. Redistributions of source code must retain the above copyright notice, this /// list of conditions and the following disclaimer. /// /// 2. Redistributions in binary form must reproduce the above copyright notice, /// this list of conditions and the following disclaimer in the documentation /// and/or other materials provided with the distribution. /// /// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" /// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE /// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE /// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE /// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL /// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR /// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER /// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, /// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE /// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #ifndef TILESON_TILESON_H #define TILESON_TILESON_H /*** Start of inlined file: json11.hpp ***/ /*** Start of inlined file: json11.cpp ***/ /*** Start of inlined file: json11.hpp ***/ /* Copyright (c) 2013 Dropbox, Inc. * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ #pragma once #include #include #include #include #include #define JSON11_IS_DEFINED #ifdef _MSC_VER #if _MSC_VER <= 1800 // VS 2013 #ifndef noexcept #define noexcept throw() #endif #ifndef snprintf #define snprintf _snprintf_s #endif #endif #endif namespace json11 { enum JsonParse { STANDARD, COMMENTS }; class JsonValue; class Json final { public: // Types enum Type { NUL, NUMBER, BOOL, STRING, ARRAY, OBJECT }; // Array and object typedefs typedef std::vector array; typedef std::map object; // Constructors for the various types of JSON value. inline Json() noexcept; // NUL inline Json(std::nullptr_t) noexcept; // NUL inline Json(double value); // NUMBER inline Json(int value); // NUMBER inline Json(bool value); // BOOL inline Json(const std::string &value); // STRING inline Json(std::string &&value); // STRING inline Json(const char * value); // STRING inline Json(const array &values); // ARRAY inline Json(array &&values); // ARRAY inline Json(const object &values); // OBJECT inline Json(object &&values); // OBJECT // Implicit constructor: anything with a to_json() function. template inline Json(const T & t) : Json(t.to_json()) {} // Implicit constructor: map-like objects (std::map, std::unordered_map, etc) template ().begin()->first)>::value && std::is_constructible().begin()->second)>::value, int>::type = 0> inline Json(const M & m) : Json(object(m.begin(), m.end())) {} // Implicit constructor: vector-like objects (std::list, std::vector, std::set, etc) template ().begin())>::value, int>::type = 0> inline Json(const V & v) : Json(array(v.begin(), v.end())) {} // This prevents Json(some_pointer) from accidentally producing a bool. Use // Json(bool(some_pointer)) if that behavior is desired. Json(void *) = delete; // Accessors inline Type type() const; inline bool is_null() const { return type() == NUL; } inline bool is_number() const { return type() == NUMBER; } inline bool is_bool() const { return type() == BOOL; } inline bool is_string() const { return type() == STRING; } inline bool is_array() const { return type() == ARRAY; } inline bool is_object() const { return type() == OBJECT; } // Return the enclosed value if this is a number, 0 otherwise. Note that json11 does not // distinguish between integer and non-integer numbers - number_value() and int_value() // can both be applied to a NUMBER-typed object. inline double number_value() const; inline int int_value() const; // Return the enclosed value if this is a boolean, false otherwise. inline bool bool_value() const; // Return the enclosed string if this is a string, "" otherwise. inline const std::string &string_value() const; // Return the enclosed std::vector if this is an array, or an empty vector otherwise. inline const array &array_items() const; // Return the enclosed std::map if this is an object, or an empty map otherwise. inline const object &object_items() const; // Return a reference to arr[i] if this is an array, Json() otherwise. inline const Json & operator[](size_t i) const; // Return a reference to obj[key] if this is an object, Json() otherwise. inline const Json & operator[](const std::string &key) const; // Serialize. inline void dump(std::string &out) const; inline std::string dump() const { std::string out; dump(out); return out; } // Parse. If parse fails, return Json() and assign an error message to err. static inline Json parse(const std::string & in, std::string & err, JsonParse strategy = JsonParse::STANDARD); static inline Json parse(const char * in, std::string & err, JsonParse strategy = JsonParse::STANDARD) { if (in) { return parse(std::string(in), err, strategy); } else { err = "null input"; return nullptr; } } // Parse multiple objects, concatenated or separated by whitespace static inline std::vector parse_multi( const std::string & in, std::string::size_type & parser_stop_pos, std::string & err, JsonParse strategy = JsonParse::STANDARD); static inline std::vector parse_multi( const std::string & in, std::string & err, JsonParse strategy = JsonParse::STANDARD) { std::string::size_type parser_stop_pos; return parse_multi(in, parser_stop_pos, err, strategy); } inline bool operator== (const Json &rhs) const; inline bool operator< (const Json &rhs) const; inline bool operator!= (const Json &rhs) const { return !(*this == rhs); } inline bool operator<= (const Json &rhs) const { return !(rhs < *this); } inline bool operator> (const Json &rhs) const { return (rhs < *this); } inline bool operator>= (const Json &rhs) const { return !(*this < rhs); } /* has_shape(types, err) * * Return true if this is a JSON object and, for each item in types, has a field of * the given type. If not, return false and set err to a descriptive message. */ typedef std::initializer_list> shape; inline bool has_shape(const shape & types, std::string & err) const; private: std::shared_ptr m_ptr; }; // Internal class hierarchy - JsonValue objects are not exposed to users of this API. class JsonValue { protected: friend class Json; friend class JsonInt; friend class JsonDouble; virtual Json::Type type() const = 0; virtual bool equals(const JsonValue * other) const = 0; virtual bool less(const JsonValue * other) const = 0; virtual void dump(std::string &out) const = 0; virtual double number_value() const; virtual int int_value() const; virtual bool bool_value() const; virtual const std::string &string_value() const; virtual const Json::array &array_items() const; virtual const Json &operator[](size_t i) const; virtual const Json::object &object_items() const; virtual const Json &operator[](const std::string &key) const; virtual ~JsonValue() {} }; } // namespace json11 /*** End of inlined file: json11.hpp ***/ #include #include #include #include #include namespace json11 { static const int max_depth = 200; using std::string; using std::vector; using std::map; using std::make_shared; using std::initializer_list; using std::move; /* Helper for representing null - just a do-nothing struct, plus comparison * operators so the helpers in JsonValue work. We can't use nullptr_t because * it may not be orderable. */ struct NullStruct { bool operator==(NullStruct) const { return true; } bool operator<(NullStruct) const { return false; } }; /* * * * * * * * * * * * * * * * * * * * * Serialization */ static void dump(NullStruct, string &out) { out += "null"; } static void dump(double value, string &out) { if (std::isfinite(value)) { char buf[32]; snprintf(buf, sizeof buf, "%.17g", value); out += buf; } else { out += "null"; } } static void dump(int value, string &out) { char buf[32]; snprintf(buf, sizeof buf, "%d", value); out += buf; } static void dump(bool value, string &out) { out += value ? "true" : "false"; } static void dump(const string &value, string &out) { out += '"'; for (size_t i = 0; i < value.length(); i++) { const char ch = value[i]; if (ch == '\\') { out += "\\\\"; } else if (ch == '"') { out += "\\\""; } else if (ch == '\b') { out += "\\b"; } else if (ch == '\f') { out += "\\f"; } else if (ch == '\n') { out += "\\n"; } else if (ch == '\r') { out += "\\r"; } else if (ch == '\t') { out += "\\t"; } else if (static_cast(ch) <= 0x1f) { char buf[8]; snprintf(buf, sizeof buf, "\\u%04x", ch); out += buf; } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 && static_cast(value[i+2]) == 0xa8) { out += "\\u2028"; i += 2; } else if (static_cast(ch) == 0xe2 && static_cast(value[i+1]) == 0x80 && static_cast(value[i+2]) == 0xa9) { out += "\\u2029"; i += 2; } else { out += ch; } } out += '"'; } static void dump(const Json::array &values, string &out) { bool first = true; out += "["; for (const auto &value : values) { if (!first) out += ", "; value.dump(out); first = false; } out += "]"; } static void dump(const Json::object &values, string &out) { bool first = true; out += "{"; for (const auto &kv : values) { if (!first) out += ", "; dump(kv.first, out); out += ": "; kv.second.dump(out); first = false; } out += "}"; } void Json::dump(string &out) const { m_ptr->dump(out); } /* * * * * * * * * * * * * * * * * * * * * Value wrappers */ template class Value : public JsonValue { protected: // Constructors explicit Value(const T &value) : m_value(value) {} explicit Value(T &&value) : m_value(move(value)) {} // Get type tag Json::Type type() const override { return tag; } // Comparisons bool equals(const JsonValue * other) const override { return m_value == static_cast *>(other)->m_value; } bool less(const JsonValue * other) const override { return m_value < static_cast *>(other)->m_value; } const T m_value; void dump(string &out) const override { json11::dump(m_value, out); } }; class JsonDouble final : public Value { double number_value() const override { return m_value; } int int_value() const override { return static_cast(m_value); } bool equals(const JsonValue * other) const override { return m_value == other->number_value(); } bool less(const JsonValue * other) const override { return m_value < other->number_value(); } public: explicit JsonDouble(double value) : Value(value) {} }; class JsonInt final : public Value { double number_value() const override { return m_value; } int int_value() const override { return m_value; } bool equals(const JsonValue * other) const override { return m_value == other->number_value(); } bool less(const JsonValue * other) const override { return m_value < other->number_value(); } public: explicit JsonInt(int value) : Value(value) {} }; class JsonBoolean final : public Value { bool bool_value() const override { return m_value; } public: explicit JsonBoolean(bool value) : Value(value) {} }; class JsonString final : public Value { const string &string_value() const override { return m_value; } public: explicit JsonString(const string &value) : Value(value) {} explicit JsonString(string &&value) : Value(move(value)) {} }; class JsonArray final : public Value { const Json::array &array_items() const override { return m_value; } const Json & operator[](size_t i) const override; public: explicit JsonArray(const Json::array &value) : Value(value) {} explicit JsonArray(Json::array &&value) : Value(move(value)) {} }; class JsonObject final : public Value { const Json::object &object_items() const override { return m_value; } const Json & operator[](const string &key) const override; public: explicit JsonObject(const Json::object &value) : Value(value) {} explicit JsonObject(Json::object &&value) : Value(move(value)) {} }; class JsonNull final : public Value { public: JsonNull() : Value({}) {} }; /* * * * * * * * * * * * * * * * * * * * * Static globals - static-init-safe */ struct Statics { const std::shared_ptr null = make_shared(); const std::shared_ptr t = make_shared(true); const std::shared_ptr f = make_shared(false); const string empty_string; const vector empty_vector; const map empty_map; Statics() {} }; static const Statics & statics() { static const Statics s {}; return s; } static const Json & static_null() { // This has to be separate, not in Statics, because Json() accesses statics().null. static const Json json_null; return json_null; } /* * * * * * * * * * * * * * * * * * * * * Constructors */ Json::Json() noexcept : m_ptr(statics().null) {} Json::Json(std::nullptr_t) noexcept : m_ptr(statics().null) {} Json::Json(double value) : m_ptr(make_shared(value)) {} Json::Json(int value) : m_ptr(make_shared(value)) {} Json::Json(bool value) : m_ptr(value ? statics().t : statics().f) {} Json::Json(const string &value) : m_ptr(make_shared(value)) {} Json::Json(string &&value) : m_ptr(make_shared(move(value))) {} Json::Json(const char * value) : m_ptr(make_shared(value)) {} Json::Json(const Json::array &values) : m_ptr(make_shared(values)) {} Json::Json(Json::array &&values) : m_ptr(make_shared(move(values))) {} Json::Json(const Json::object &values) : m_ptr(make_shared(values)) {} Json::Json(Json::object &&values) : m_ptr(make_shared(move(values))) {} /* * * * * * * * * * * * * * * * * * * * * Accessors */ inline Json::Type Json::type() const { return m_ptr->type(); } inline double Json::number_value() const { return m_ptr->number_value(); } inline int Json::int_value() const { return m_ptr->int_value(); } inline bool Json::bool_value() const { return m_ptr->bool_value(); } inline const string & Json::string_value() const { return m_ptr->string_value(); } inline const vector & Json::array_items() const { return m_ptr->array_items(); } inline const map & Json::object_items() const { return m_ptr->object_items(); } inline const Json & Json::operator[] (size_t i) const { return (*m_ptr)[i]; } inline const Json & Json::operator[] (const string &key) const { return (*m_ptr)[key]; } inline double JsonValue::number_value() const { return 0; } inline int JsonValue::int_value() const { return 0; } inline bool JsonValue::bool_value() const { return false; } inline const string & JsonValue::string_value() const { return statics().empty_string; } inline const vector & JsonValue::array_items() const { return statics().empty_vector; } inline const map & JsonValue::object_items() const { return statics().empty_map; } inline const Json & JsonValue::operator[] (size_t) const { return static_null(); } inline const Json & JsonValue::operator[] (const string &) const { return static_null(); } inline const Json & JsonObject::operator[] (const string &key) const { auto iter = m_value.find(key); return (iter == m_value.end()) ? static_null() : iter->second; } inline const Json & JsonArray::operator[] (size_t i) const { if (i >= m_value.size()) return static_null(); else return m_value[i]; } /* * * * * * * * * * * * * * * * * * * * * Comparison */ bool Json::operator== (const Json &other) const { if (m_ptr == other.m_ptr) return true; if (m_ptr->type() != other.m_ptr->type()) return false; return m_ptr->equals(other.m_ptr.get()); } bool Json::operator< (const Json &other) const { if (m_ptr == other.m_ptr) return false; if (m_ptr->type() != other.m_ptr->type()) return m_ptr->type() < other.m_ptr->type(); return m_ptr->less(other.m_ptr.get()); } /* * * * * * * * * * * * * * * * * * * * * Parsing */ /* esc(c) * * Format char c suitable for printing in an error message. */ static inline string esc(char c) { char buf[12]; if (static_cast(c) >= 0x20 && static_cast(c) <= 0x7f) { snprintf(buf, sizeof buf, "'%c' (%d)", c, c); } else { snprintf(buf, sizeof buf, "(%d)", c); } return string(buf); } static inline bool in_range(long x, long lower, long upper) { return (x >= lower && x <= upper); } namespace { /* JsonParser * * Object that tracks all state of an in-progress parse. */ struct JsonParser final { /* State */ const string &str; size_t i; string &err; bool failed; const JsonParse strategy; /* fail(msg, err_ret = Json()) * * Mark this parse as failed. */ Json fail(string &&msg) { return fail(move(msg), Json()); } template T fail(string &&msg, const T err_ret) { if (!failed) err = std::move(msg); failed = true; return err_ret; } /* consume_whitespace() * * Advance until the current character is non-whitespace. */ void consume_whitespace() { while (str[i] == ' ' || str[i] == '\r' || str[i] == '\n' || str[i] == '\t') i++; } /* consume_comment() * * Advance comments (c-style inline and multiline). */ bool consume_comment() { bool comment_found = false; if (str[i] == '/') { i++; if (i == str.size()) return fail("unexpected end of input after start of comment", false); if (str[i] == '/') { // inline comment i++; // advance until next line, or end of input while (i < str.size() && str[i] != '\n') { i++; } comment_found = true; } else if (str[i] == '*') { // multiline comment i++; if (i > str.size()-2) return fail("unexpected end of input inside multi-line comment", false); // advance until closing tokens while (!(str[i] == '*' && str[i+1] == '/')) { i++; if (i > str.size()-2) return fail( "unexpected end of input inside multi-line comment", false); } i += 2; comment_found = true; } else return fail("malformed comment", false); } return comment_found; } /* consume_garbage() * * Advance until the current character is non-whitespace and non-comment. */ void consume_garbage() { consume_whitespace(); if(strategy == JsonParse::COMMENTS) { bool comment_found = false; do { comment_found = consume_comment(); if (failed) return; consume_whitespace(); } while(comment_found); } } /* get_next_token() * * Return the next non-whitespace character. If the end of the input is reached, * flag an error and return 0. */ char get_next_token() { consume_garbage(); if (failed) return static_cast(0); if (i == str.size()) return fail("unexpected end of input", static_cast(0)); return str[i++]; } /* encode_utf8(pt, out) * * Encode pt as UTF-8 and add it to out. */ void encode_utf8(long pt, string & out) { if (pt < 0) return; if (pt < 0x80) { out += static_cast(pt); } else if (pt < 0x800) { out += static_cast((pt >> 6) | 0xC0); out += static_cast((pt & 0x3F) | 0x80); } else if (pt < 0x10000) { out += static_cast((pt >> 12) | 0xE0); out += static_cast(((pt >> 6) & 0x3F) | 0x80); out += static_cast((pt & 0x3F) | 0x80); } else { out += static_cast((pt >> 18) | 0xF0); out += static_cast(((pt >> 12) & 0x3F) | 0x80); out += static_cast(((pt >> 6) & 0x3F) | 0x80); out += static_cast((pt & 0x3F) | 0x80); } } /* parse_string() * * Parse a string, starting at the current position. */ string parse_string() { string out; long last_escaped_codepoint = -1; while (true) { if (i == str.size()) return fail("unexpected end of input in string", ""); char ch = str[i++]; if (ch == '"') { encode_utf8(last_escaped_codepoint, out); return out; } if (in_range(ch, 0, 0x1f)) return fail("unescaped " + esc(ch) + " in string", ""); // The usual case: non-escaped characters if (ch != '\\') { encode_utf8(last_escaped_codepoint, out); last_escaped_codepoint = -1; out += ch; continue; } // Handle escapes if (i == str.size()) return fail("unexpected end of input in string", ""); ch = str[i++]; if (ch == 'u') { // Extract 4-byte escape sequence string esc = str.substr(i, 4); // Explicitly check length of the substring. The following loop // relies on std::string returning the terminating NUL when // accessing str[length]. Checking here reduces brittleness. if (esc.length() < 4) { return fail("bad \\u escape: " + esc, ""); } for (size_t j = 0; j < 4; j++) { if (!in_range(esc[j], 'a', 'f') && !in_range(esc[j], 'A', 'F') && !in_range(esc[j], '0', '9')) return fail("bad \\u escape: " + esc, ""); } long codepoint = strtol(esc.data(), nullptr, 16); // JSON specifies that characters outside the BMP shall be encoded as a pair // of 4-hex-digit \u escapes encoding their surrogate pair components. Check // whether we're in the middle of such a beast: the previous codepoint was an // escaped lead (high) surrogate, and this is a trail (low) surrogate. if (in_range(last_escaped_codepoint, 0xD800, 0xDBFF) && in_range(codepoint, 0xDC00, 0xDFFF)) { // Reassemble the two surrogate pairs into one astral-plane character, per // the UTF-16 algorithm. encode_utf8((((last_escaped_codepoint - 0xD800) << 10) | (codepoint - 0xDC00)) + 0x10000, out); last_escaped_codepoint = -1; } else { encode_utf8(last_escaped_codepoint, out); last_escaped_codepoint = codepoint; } i += 4; continue; } encode_utf8(last_escaped_codepoint, out); last_escaped_codepoint = -1; if (ch == 'b') { out += '\b'; } else if (ch == 'f') { out += '\f'; } else if (ch == 'n') { out += '\n'; } else if (ch == 'r') { out += '\r'; } else if (ch == 't') { out += '\t'; } else if (ch == '"' || ch == '\\' || ch == '/') { out += ch; } else { return fail("invalid escape character " + esc(ch), ""); } } } /* parse_number() * * Parse a double. */ Json parse_number() { size_t start_pos = i; if (str[i] == '-') i++; // Integer part if (str[i] == '0') { i++; if (in_range(str[i], '0', '9')) return fail("leading 0s not permitted in numbers"); } else if (in_range(str[i], '1', '9')) { i++; while (in_range(str[i], '0', '9')) i++; } else { return fail("invalid " + esc(str[i]) + " in number"); } if (str[i] != '.' && str[i] != 'e' && str[i] != 'E' && (i - start_pos) <= static_cast(std::numeric_limits::digits10)) { return std::atoi(str.c_str() + start_pos); } // Decimal part if (str[i] == '.') { i++; if (!in_range(str[i], '0', '9')) return fail("at least one digit required in fractional part"); while (in_range(str[i], '0', '9')) i++; } // Exponent part if (str[i] == 'e' || str[i] == 'E') { i++; if (str[i] == '+' || str[i] == '-') i++; if (!in_range(str[i], '0', '9')) return fail("at least one digit required in exponent"); while (in_range(str[i], '0', '9')) i++; } return std::strtod(str.c_str() + start_pos, nullptr); } /* expect(str, res) * * Expect that 'str' starts at the character that was just read. If it does, advance * the input and return res. If not, flag an error. */ Json expect(const string &expected, Json res) { assert(i != 0); i--; if (str.compare(i, expected.length(), expected) == 0) { i += expected.length(); return res; } else { return fail("parse error: expected " + expected + ", got " + str.substr(i, expected.length())); } } /* parse_json() * * Parse a JSON object. */ Json parse_json(int depth) { if (depth > max_depth) { return fail("exceeded maximum nesting depth"); } char ch = get_next_token(); if (failed) return Json(); if (ch == '-' || (ch >= '0' && ch <= '9')) { i--; return parse_number(); } if (ch == 't') return expect("true", true); if (ch == 'f') return expect("false", false); if (ch == 'n') return expect("null", Json()); if (ch == '"') return parse_string(); if (ch == '{') { map data; ch = get_next_token(); if (ch == '}') return data; while (1) { if (ch != '"') return fail("expected '\"' in object, got " + esc(ch)); string key = parse_string(); if (failed) return Json(); ch = get_next_token(); if (ch != ':') return fail("expected ':' in object, got " + esc(ch)); data[std::move(key)] = parse_json(depth + 1); if (failed) return Json(); ch = get_next_token(); if (ch == '}') break; if (ch != ',') return fail("expected ',' in object, got " + esc(ch)); ch = get_next_token(); } return data; } if (ch == '[') { vector data; ch = get_next_token(); if (ch == ']') return data; while (1) { i--; data.push_back(parse_json(depth + 1)); if (failed) return Json(); ch = get_next_token(); if (ch == ']') break; if (ch != ',') return fail("expected ',' in list, got " + esc(ch)); ch = get_next_token(); (void)ch; } return data; } return fail("expected value, got " + esc(ch)); } }; }//namespace { Json Json::parse(const string &in, string &err, JsonParse strategy) { JsonParser parser { in, 0, err, false, strategy }; Json result = parser.parse_json(0); // Check for any trailing garbage parser.consume_garbage(); if (parser.failed) return Json(); if (parser.i != in.size() && ((parser.i + 1) != in.size() && in[parser.i] != 0)) //RBP: If there is only 1 character diff, it is probably just a terminating zero from a memory read. { return parser.fail("unexpected trailing " + esc(in[parser.i])); } return result; } // Documented in json11.hpp vector Json::parse_multi(const string &in, std::string::size_type &parser_stop_pos, string &err, JsonParse strategy) { JsonParser parser { in, 0, err, false, strategy }; parser_stop_pos = 0; vector json_vec; while (parser.i != in.size() && !parser.failed) { json_vec.push_back(parser.parse_json(0)); if (parser.failed) break; // Check for another object parser.consume_garbage(); if (parser.failed) break; parser_stop_pos = parser.i; } return json_vec; } /* * * * * * * * * * * * * * * * * * * * * Shape-checking */ bool Json::has_shape(const shape & types, string & err) const { if (!is_object()) { err = "expected JSON object, got " + dump(); return false; } const auto& obj_items = object_items(); for (auto & item : types) { const auto it = obj_items.find(item.first); if (it == obj_items.cend() || it->second.type() != item.second) { err = "bad type for " + item.first + " in " + dump(); return false; } } return true; } } // namespace json11 /*** End of inlined file: json11.cpp ***/ /*** End of inlined file: json11.hpp ***/ /*** Start of inlined file: tileson_parser.hpp ***/ // // Created by robin on 22.03.2020. // #ifndef TILESON_TILESON_PARSER_HPP #define TILESON_TILESON_PARSER_HPP //RBP: FS-namespace is defined in tileson_parser now! #if _MSC_VER && !__INTEL_COMPILER #include namespace fs = std::filesystem; #elif __MINGW64__ #if __MINGW64_VERSION_MAJOR > 6 #include namespace fs = std::filesystem; #else #include namespace fs = std::experimental::filesystem; #endif #elif __clang__ #if __clang_major__ < 8 #include namespace fs = std::experimental::filesystem; #else #include namespace fs = std::filesystem; #endif #else //Linux #if __GNUC__ < 8 //GCC major version less than 8 #include namespace fs = std::experimental::filesystem; #else #include namespace fs = std::filesystem; #endif #endif #include #include #include /*** Start of inlined file: Tools.hpp ***/ // // Created by robin on 31.07.2020. // #ifndef TILESON_TOOLS_HPP #define TILESON_TOOLS_HPP #include #include #include namespace tson { class Tools { public: Tools() = delete; ~Tools() = delete; inline static std::vector Base64DecodedStringToBytes(std::string_view str); inline static std::vector BytesToUnsignedInts(const std::vector &bytes); //inline static std::vector BytesToInts(const std::vector &bytes); }; /*! * When you have decoded a Base64 string, you'll get a string representing bytes. This function turns them into actual bytes. * @param str * @return */ std::vector Tools::Base64DecodedStringToBytes(std::string_view str) { std::vector bytes; for(size_t i = 0; i < str.size(); ++i) { uint8_t u8 = static_cast(str[i]); bytes.push_back(u8); } return bytes; } /*! * Converts bytes into unsigned int values. The bytes are converted in the Little Endian byte order to fit Tiled's specs. * @param bytes A vector of bytes. * @return Bytes converted to unsigned ints */ std::vector Tools::BytesToUnsignedInts(const std::vector &bytes) { std::vector uints; std::vector toConvert; //uint32_t size8 = (compressed[55] << 24) | (compressed[56] << 16) | (compressed[57] << 8) | compressed[58]; //Should be 66000 for(size_t i = 0; i < bytes.size(); ++i) { toConvert.push_back(bytes[i]); if(toConvert.size() == 4) { uint32_t u32 = (toConvert[3] << 24) | (toConvert[2] << 16) | (toConvert[1] << 8) | toConvert[0]; uints.push_back(u32); toConvert.clear(); } } return uints; } /*! * While the Tiled specification uses unsigned ints for their tiles, Tileson uses regular ints. * This may be changed in the future, but should in reality never really become an issue. * * Update 2020-11-09: This will cause problems when tiles has flip flags! * * int differences: * int max: 2147483647 * uint max: 4294967295 * * @param bytes A vector of bytes. * @return Bytes converted to ints */ /*std::vector Tools::BytesToInts(const std::vector &bytes) { std::vector ints; std::vector toConvert; //uint32_t size8 = (compressed[55] << 24) | (compressed[56] << 16) | (compressed[57] << 8) | compressed[58]; //Should be 66000 for(size_t i = 0; i < bytes.size(); ++i) { toConvert.push_back(bytes[i]); if(toConvert.size() == 4) { uint32_t u32 = (toConvert[3] << 24) | (toConvert[2] << 16) | (toConvert[1] << 8) | toConvert[0]; ints.push_back(u32); toConvert.clear(); } } return ints; }*/ } #endif //TILESON_TOOLS_HPP /*** End of inlined file: Tools.hpp ***/ /*** Start of inlined file: Base64Decompressor.hpp ***/ // // Created by robin on 29.07.2020. // The Base64 decoding logic is heavily based on: https://github.com/ReneNyffenegger/cpp-base64 // #ifndef TILESON_BASE64DECOMPRESSOR_HPP #define TILESON_BASE64DECOMPRESSOR_HPP /*** Start of inlined file: IDecompressor.hpp ***/ // // Created by robin on 29.07.2020. // #ifndef TILESON_IDECOMPRESSOR_HPP #define TILESON_IDECOMPRESSOR_HPP #include namespace tson { template class IDecompressor { public: /*! * If the name matches with 'compression' or 'encoding' the decompress() function will * be called automatically for the actual Layer. Encoding-related matching is handled first! * * Known values: * * compression: zlib, gzip, zstd (since Tiled 1.3) or empty (default) (tilelayer only). * encoding: csv (default) or base64 (tilelayer only). * * @return */ [[nodiscard]] virtual const std::string &name() const = 0; /*! * Used primarily for Tiled related decompression. * @param input Input data * @return Decompressed data */ virtual TOut decompress(const TIn &input) = 0; /*! * Used for whole file decompression. Not related to Tiled * @param path * @return */ virtual TOut decompressFile(const fs::path &path) = 0; /*! * Used for whole file decompression. Not related to Tiled * @param path * @return */ virtual TOut decompress(const void *data, size_t size) = 0; }; } #endif //TILESON_IDECOMPRESSOR_HPP /*** End of inlined file: IDecompressor.hpp ***/ #include namespace tson { class Base64Decompressor : public IDecompressor { public: [[nodiscard]] inline const std::string &name() const override; inline std::string decompress(const std::string_view &s) override; inline std::string decompressFile(const fs::path &path) override; inline std::string decompress(const void *data, size_t size) override; private: inline unsigned int pos_of_char(const unsigned char chr); inline static const std::string NAME = "base64"; }; const std::string &Base64Decompressor::name() const { return NAME; } std::string Base64Decompressor::decompress(const std::string_view &s) { size_t length_of_string = s.length(); if (!length_of_string) return std::string(""); size_t in_len = length_of_string; size_t pos = 0; // // The approximate length (bytes) of the decoded string might be one ore // 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 < in_len) { unsigned int pos_of_char_1 = pos_of_char(s[pos+1] ); ret.push_back(static_cast( ( (pos_of_char(s[pos+0]) ) << 2 ) + ( (pos_of_char_1 & 0x30 ) >> 4))); if (s[pos+2] != '=' && s[pos+2] != '.') { // accept URL-safe base 64 strings, too, so check for '.' also. unsigned int pos_of_char_2 = pos_of_char(s[pos+2] ); ret.push_back(static_cast( (( pos_of_char_1 & 0x0f) << 4) + (( pos_of_char_2 & 0x3c) >> 2))); if (s[pos+3] != '=' && s[pos+3] != '.') { ret.push_back(static_cast( ( (pos_of_char_2 & 0x03 ) << 6 ) + pos_of_char(s[pos+3]) )); } } pos += 4; } return ret; } unsigned int Base64Decompressor::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 '_' throw "If input is correct, this line should never be reached."; } /*! * UNUSED! Does nothing * @param path * @return */ std::string Base64Decompressor::decompressFile(const fs::path &path) { return std::string(); } /*! * UNUSED! Does nothing * @param path * @return */ std::string Base64Decompressor::decompress(const void *data, size_t size) { return std::string(); } } #endif //TILESON_BASE64DECOMPRESSOR_HPP /*** End of inlined file: Base64Decompressor.hpp ***/ /*** Start of inlined file: Lzma.hpp ***/ // // Created by robin on 16.01.2021. // //#include "../../extras/pocketlzma.hpp" #ifdef POCKETLZMA_POCKETLZMA_H #ifndef TILESON_LZMA_HPP #define TILESON_LZMA_HPP namespace tson { class Lzma : public IDecompressor, std::vector> { public: inline const std::string &name() const override { return NAME; } inline std::vector decompress(const std::vector &input) override { std::vector out; plz::PocketLzma p; plz::StatusCode status = p.decompress(input, out); if(status != plz::StatusCode::Ok) return std::vector(); return out; } inline std::vector decompressFile(const fs::path &path) override { std::vector in; std::vector out; plz::PocketLzma p; plz::FileStatus fileStatus = plz::File::FromFile(path.u8string(), in); if(fileStatus.status() != plz::FileStatus::Code::Ok) return std::vector(); plz::StatusCode status = p.decompress(in, out); if(status != plz::StatusCode::Ok) return std::vector(); return out; } inline std::vector decompress(const void *data, size_t size) override { std::vector out; plz::PocketLzma p; plz::StatusCode status = p.decompress((uint8_t*) data, size, out); if(status != plz::StatusCode::Ok) return std::vector(); return out; } private: inline static const std::string NAME {"lzma"}; }; } #endif //TILESON_LZMA_HPP #endif /*** End of inlined file: Lzma.hpp ***/ /*** Start of inlined file: DecompressorContainer.hpp ***/ // // Created by robin on 30.07.2020. // #ifndef TILESON_DECOMPRESSORCONTAINER_HPP #define TILESON_DECOMPRESSORCONTAINER_HPP #include #include #include #include namespace tson { class DecompressorContainer { public: inline DecompressorContainer() = default; template inline void add(Args &&... args); inline void remove(std::string_view name); inline bool contains(std::string_view name) const; inline bool empty() const; inline size_t size() const; inline void clear(); inline IDecompressor *get(std::string_view name); private: //Key: name, std::vector>> m_decompressors; }; template void DecompressorContainer::add(Args &&... args) { m_decompressors.emplace_back(new T(args...)); } /*! * * @param name The name of the decompressor to check whether exists. * @return Whether a decompressor with the given name exists or not. */ bool DecompressorContainer::contains(std::string_view name) const { auto iter = std::find_if(m_decompressors.begin(), m_decompressors.end(), [&](const auto &item) { return item->name() == name; }); return iter != m_decompressors.end(); } /*! * Removed an element with the given name. * @param name The name of the decompressor */ void DecompressorContainer::remove(std::string_view name) { auto iter = std::remove_if(m_decompressors.begin(), m_decompressors.end(), [&](const auto &item) { return item->name() == name; }); m_decompressors.erase(iter); } size_t DecompressorContainer::size() const { return m_decompressors.size(); } /*! * * @param name The name of the container * @return An ICompressor pointer if it exists. nullptr otherwise. */ IDecompressor *DecompressorContainer::get(std::string_view name) { auto iter = std::find_if(m_decompressors.begin(), m_decompressors.end(), [&](const auto &item) { return item->name() == name; }); return (iter != m_decompressors.end()) ? iter->get() : nullptr; } /*! * Check if container is empty * @return Whether or not the container is empty */ bool DecompressorContainer::empty() const { return m_decompressors.empty(); } /*! * Clears all IDecompressor elements in the container */ void DecompressorContainer::clear() { m_decompressors.clear(); } } #endif //TILESON_DECOMPRESSORCONTAINER_HPP /*** End of inlined file: DecompressorContainer.hpp ***/ /*** Start of inlined file: MemoryStream.hpp ***/ // // Created by robin on 22.03.2020. // #ifndef TILESON_MEMORYSTREAM_HPP #define TILESON_MEMORYSTREAM_HPP /*** Start of inlined file: MemoryBuffer.hpp ***/ // // Created by robin on 22.03.2020. // #ifndef TILESON_MEMORYBUFFER_HPP #define TILESON_MEMORYBUFFER_HPP #include namespace tson { class MemoryBuffer : public std::basic_streambuf { public: MemoryBuffer(const uint8_t *p, size_t l) { setg((char*)p, (char*)p, (char*)p + l); } }; } #endif //TILESON_MEMORYBUFFER_HPP /*** End of inlined file: MemoryBuffer.hpp ***/ namespace tson { class MemoryStream : public std::istream { public: MemoryStream(const uint8_t *p, size_t l) : std::istream(&m_buffer), m_buffer(p, l) { rdbuf(&m_buffer); } private: MemoryBuffer m_buffer; }; } #endif //TILESON_MEMORYSTREAM_HPP /*** End of inlined file: MemoryStream.hpp ***/ /*** Start of inlined file: Map.hpp ***/ // // Created by robin on 22.03.2020. // #ifndef TILESON_MAP_HPP #define TILESON_MAP_HPP /*** Start of inlined file: Color.hpp ***/ // // Created by robin on 09.08.2019. // #ifndef TILESON_COLOR_HPP #define TILESON_COLOR_HPP #include #include #include namespace tson { template class Color { public: /*! * Parses color from Tiled's own color format, which is #aarrggbb in hex format or optionally #rrggbb. * @param color Color in "#rrggbbaa" hex format. * @example "#ffaa07ff" and "#aa07ff". In cases where alpha is not a value, it is set to 255. */ inline explicit Color(const std::string &color) { parseHexString(color); } inline Color(T red, T green, T blue, T alpha); inline Color() { r = g = b = 0; a = 255; } inline bool operator==(const Color &rhs) const; inline bool operator==(const std::string &rhs) const; inline bool operator!=(const Color &rhs) const; inline Color asFloat(); inline Color asInt(); /*! Red */ T r; /*! Green */ T g; /*! Blue */ T b; /*! Alpha */ T a; private: void parseHexString(const std::string &color) { if constexpr (std::is_same::value) { if (color.size() == 9) { a = (float) std::stoi(color.substr(1, 2), nullptr, 16) / 255; r = (float) std::stoi(color.substr(3, 2), nullptr, 16) / 255; g = (float) std::stoi(color.substr(5, 2), nullptr, 16) / 255; b = (float) std::stoi(color.substr(7, 2), nullptr, 16) / 255; } else if (color.size() == 7) { r = (float) std::stoi(color.substr(1, 2), nullptr, 16) / 255; g = (float) std::stoi(color.substr(3, 2), nullptr, 16) / 255; b = (float) std::stoi(color.substr(5, 2), nullptr, 16) / 255; a = 1.f; } } else { if (color.size() == 9) { a = std::stoi(color.substr(1, 2), nullptr, 16); r = std::stoi(color.substr(3, 2), nullptr, 16); g = std::stoi(color.substr(5, 2), nullptr, 16); b = std::stoi(color.substr(7, 2), nullptr, 16); } else if (color.size() == 7) { r = std::stoi(color.substr(1, 2), nullptr, 16); g = std::stoi(color.substr(3, 2), nullptr, 16); b = std::stoi(color.substr(5, 2), nullptr, 16); a = 255; } } } }; typedef Color Colori; typedef Color Colorf; /*! * Gets the Color as a float. Only useful if the template related to the current color is NOT float * @tparam T The template type * @return If the T type is float, the value will be returned as a copy of itself. Else: All values will be divided by 255 * before returning. */ template tson::Colorf Color::asFloat() { if constexpr (std::is_same::value) *this; else return tson::Colorf((float) r / 255, (float) g / 255, (float) b / 255, (float) a / 255); } /*! * Gets the Color as an 32-bit variable, where each channel is 8-bit. * Only useful if the template related to the current color is NOT already 8-bit int * @tparam T The template type * @return If the T type is float, the value of each channel will be multiplied by 255. Else: The value will be returned as a copy of itself. */ template tson::Colori Color::asInt() { if constexpr (std::is_same::value) return tson::Colori((float) r * 255, (float) g * 255, (float) b * 255, (float) a * 255); else *this; } /*! * Create a new color in rgba (red, green, blue, alpha) format * @tparam T the template type for each channel. Usually uint8_t (8-bit int) or float. * @param red Red channel * @param green Green channel * @param blue Blue channel * @param alpha Alpha channel */ template Color::Color(T red, T green, T blue, T alpha) { r = red; g = green; b = blue; a = alpha; } template bool Color::operator==(const std::string &rhs) const { Color other {rhs}; return *this == other; } template bool Color::operator==(const Color &rhs) const { return r == rhs.r && g == rhs.g && b == rhs.b && a == rhs.a; } template bool Color::operator!=(const Color &rhs) const { return !(rhs == *this); } } #endif //TILESON_COLOR_HPP /*** End of inlined file: Color.hpp ***/ /*** Start of inlined file: Vector2.hpp ***/ // // Created by robin on 31.07.2019. // #ifndef TILESON_VECTOR2_HPP #define TILESON_VECTOR2_HPP namespace tson { template class Vector2 { public: inline Vector2(T xPos, T yPos); inline Vector2() { x = y = 0; } inline bool operator==(const Vector2 &rhs) const; inline bool operator!=(const Vector2 &rhs) const; T x; T y; }; /*! * * @tparam T template type * @param xPos x-position * @param yPos y-position */ template Vector2::Vector2(T xPos, T yPos) { x = xPos; y = yPos; } template bool Vector2::operator==(const Vector2 &rhs) const { return x == rhs.x && y == rhs.y; } template bool Vector2::operator!=(const Vector2 &rhs) const { return !(rhs == *this); } typedef Vector2 Vector2i; typedef Vector2 Vector2f; } #endif //TILESON_VECTOR2_HPP /*** End of inlined file: Vector2.hpp ***/ //#include "../external/json.hpp" /*** Start of inlined file: IJson.hpp ***/ // // Created by robin on 06.01.2021. // #ifndef TILESON_IJSON_HPP #define TILESON_IJSON_HPP namespace tson { class IJson { public: virtual IJson& operator[](std::string_view key) = 0; virtual IJson &at(std::string_view key) = 0; virtual IJson &at(size_t pos) = 0; /*! * If current json object is an array, this will get all elements of it! * @return An array */ [[nodiscard]] virtual std::vector> array() = 0; [[nodiscard]] virtual std::vector> &array(std::string_view key) = 0; /*! * Get the size of an object. This will be equal to the number of * variables an object contains. * @return */ [[nodiscard]] virtual size_t size() const = 0; [[nodiscard]] virtual bool parse(const fs::path &path) = 0; [[nodiscard]] virtual bool parse(const void *data, size_t size) = 0; template [[nodiscard]] T get(std::string_view key); template [[nodiscard]] T get(); [[nodiscard]] virtual size_t count(std::string_view key) const = 0; [[nodiscard]] virtual bool any(std::string_view key) const = 0; [[nodiscard]] virtual bool isArray() const = 0; [[nodiscard]] virtual bool isObject() const = 0; [[nodiscard]] virtual bool isNull() const = 0; protected: [[nodiscard]] virtual int32_t getInt32(std::string_view key) = 0; [[nodiscard]] virtual uint32_t getUInt32(std::string_view key) = 0; [[nodiscard]] virtual int64_t getInt64(std::string_view key) = 0; [[nodiscard]] virtual uint64_t getUInt64(std::string_view key) = 0; [[nodiscard]] virtual double getDouble(std::string_view key) = 0; [[nodiscard]] virtual float getFloat(std::string_view key) = 0; [[nodiscard]] virtual std::string getString(std::string_view key) = 0; [[nodiscard]] virtual bool getBool(std::string_view key) = 0; [[nodiscard]] virtual int32_t getInt32() = 0; [[nodiscard]] virtual uint32_t getUInt32() = 0; [[nodiscard]] virtual int64_t getInt64() = 0; [[nodiscard]] virtual uint64_t getUInt64() = 0; [[nodiscard]] virtual double getDouble() = 0; [[nodiscard]] virtual float getFloat() = 0; [[nodiscard]] virtual std::string getString() = 0; [[nodiscard]] virtual bool getBool() = 0; }; template T IJson::get(std::string_view key) { if constexpr (std::is_same::value) return getDouble(key); if constexpr (std::is_same::value) return getFloat(key); else if constexpr (std::is_same::value) return getInt32(key); else if constexpr (std::is_same::value) return getUInt32(key); else if constexpr (std::is_same::value) return getInt64(key); else if constexpr (std::is_same::value) return getUInt64(key); else if constexpr (std::is_same::value) return getString(key); else if constexpr (std::is_same::value) return getBool(key); else return nullptr; } template T IJson::get() { if constexpr (std::is_same::value) return getDouble(); if constexpr (std::is_same::value) return getFloat(); else if constexpr (std::is_same::value) return getInt32(); else if constexpr (std::is_same::value) return getUInt32(); else if constexpr (std::is_same::value) return getInt64(); else if constexpr (std::is_same::value) return getUInt64(); else if constexpr (std::is_same::value) return getString(); else if constexpr (std::is_same::value) return getBool(); else return nullptr; } } #endif //TILESON_IJSON_HPP /*** End of inlined file: IJson.hpp ***/ /*** Start of inlined file: NlohmannJson.hpp ***/ // // Created by robin on 08.01.2021. // #ifdef INCLUDE_NLOHMANN_JSON_HPP_ #ifndef TILESON_NLOHMANNJSON_HPP #define TILESON_NLOHMANNJSON_HPP namespace tson { class NlohmannJson : public tson::IJson { public: inline NlohmannJson() = default; IJson &operator[](std::string_view key) override { if(m_arrayCache.count(key.data()) == 0) m_arrayCache[key.data()] = std::make_unique(&m_json->operator[](key.data()));//.front()); return *m_arrayCache[key.data()].get(); } inline explicit NlohmannJson(nlohmann::json *json) : m_json {json} { } inline IJson& at(std::string_view key) override { if(m_arrayCache.count(key.data()) == 0) m_arrayCache[key.data()] = std::make_unique(&m_json->operator[](key.data()));//.front()); return *m_arrayCache[key.data()].get(); } inline IJson& at(size_t pos) override { if(m_arrayPosCache.count(pos) == 0) m_arrayPosCache[pos] = std::make_unique(&m_json->at(pos)); return *m_arrayPosCache[pos]; } std::vector> array() override { std::vector> vec; for(auto &item : *m_json) { nlohmann::json *ptr = &item; vec.emplace_back(std::make_unique(ptr)); } return vec; } inline std::vector> &array(std::string_view key) override { if(m_arrayListDataCache.count(key.data()) == 0) { if (m_json->count(key.data()) > 0 && m_json->operator[](key.data()).is_array()) { std::for_each(m_json->operator[](key.data()).begin(), m_json->operator[](key.data()).end(), [&](nlohmann::json &item) { nlohmann::json *ptr = &item; m_arrayListDataCache[key.data()].emplace_back(std::make_unique(ptr)); }); } } return m_arrayListDataCache[key.data()]; } [[nodiscard]] inline size_t size() const override { return m_json->size(); } inline bool parse(const fs::path &path) override { clearCache(); m_data = nullptr; m_json = nullptr; if (fs::exists(path) && fs::is_regular_file(path)) { m_data = std::make_unique(); std::ifstream i(path.u8string()); try { i >> *m_data; m_json = m_data.get(); } catch (const nlohmann::json::parse_error &error) { std::string message = "Parse error: "; message += std::string(error.what()); message += std::string("\n"); std::cerr << message; return false; } return true; } return false; } inline bool parse(const void *data, size_t size) override { clearCache(); m_json = nullptr; m_data = std::make_unique(); tson::MemoryStream mem{(uint8_t *) data, size}; try { mem >> *m_data; m_json = m_data.get(); } catch (const nlohmann::json::parse_error &error) { std::string message = "Parse error: "; message += std::string(error.what()); message += std::string("\n"); std::cerr << message; return false; } return true; } [[nodiscard]] inline size_t count(std::string_view key) const override { return m_json->count(key); } [[nodiscard]] inline bool any(std::string_view key) const override { return count(key) > 0; } [[nodiscard]] inline bool isArray() const override { return m_json->is_array(); } [[nodiscard]] inline bool isObject() const override { return m_json->is_object(); } [[nodiscard]] inline bool isNull() const override { return m_json->is_null(); } protected: [[nodiscard]] inline int32_t getInt32(std::string_view key) override { return m_json->operator[](key.data()).get(); } [[nodiscard]] inline uint32_t getUInt32(std::string_view key) override { return m_json->operator[](key.data()).get(); } [[nodiscard]] inline int64_t getInt64(std::string_view key) override { return m_json->operator[](key.data()).get(); } [[nodiscard]] inline uint64_t getUInt64(std::string_view key) override { return m_json->operator[](key.data()).get(); } [[nodiscard]] inline double getDouble(std::string_view key) override { return m_json->operator[](key.data()).get(); } [[nodiscard]] inline std::string getString(std::string_view key) override { return m_json->operator[](key.data()).get(); } [[nodiscard]] inline bool getBool(std::string_view key) override { return m_json->operator[](key.data()).get(); } [[nodiscard]] float getFloat(std::string_view key) override { return m_json->operator[](key.data()).get(); } [[nodiscard]] inline int32_t getInt32() override { return m_json->get(); } [[nodiscard]] inline uint32_t getUInt32() override { return m_json->get(); } [[nodiscard]] inline int64_t getInt64() override { return m_json->get(); } [[nodiscard]] inline uint64_t getUInt64() override { return m_json->get(); } [[nodiscard]] inline double getDouble() override { return m_json->get(); } [[nodiscard]] inline std::string getString() override { return m_json->get(); } [[nodiscard]] inline bool getBool() override { return m_json->get(); } [[nodiscard]] float getFloat() override { return m_json->get(); } private: inline void clearCache() { m_arrayCache.clear(); m_arrayPosCache.clear(); m_arrayListDataCache.clear(); } nlohmann::json *m_json = nullptr; std::unique_ptr m_data = nullptr; //Only used if this is the owner json! //Cache! std::map> m_arrayCache; std::map> m_arrayPosCache; std::map>> m_arrayListDataCache; }; } #endif //TILESON_NLOHMANNJSON_HPP #endif //INCLUDE_NLOHMANN_JSON_HPP_ /*** End of inlined file: NlohmannJson.hpp ***/ /*** Start of inlined file: PicoJson.hpp ***/ // // Created by robin on 11.01.2021. // #ifdef picojson_h #ifndef TILESON_PICOJSON_HPP #define TILESON_PICOJSON_HPP namespace tson { class PicoJson : public tson::IJson { public: inline PicoJson() = default; IJson &operator[](std::string_view key) override { if(m_arrayCache.count(key.data()) == 0) { if(m_json->is()) { picojson::object &o = m_json->get(); m_arrayCache[key.data()] = std::make_unique(&o[key.data()]); } } return *m_arrayCache[key.data()].get(); } inline explicit PicoJson(picojson::value *json) : m_json {json} { } inline IJson& at(std::string_view key) override { if(m_arrayCache.count(key.data()) == 0) { if(m_json->is()) { picojson::object &o = m_json->get(); m_arrayCache[key.data()] = std::make_unique(&o[key.data()]); } } return *m_arrayCache[key.data()].get(); } inline IJson& at(size_t pos) override { if(m_arrayPosCache.count(pos) == 0) { picojson::array &a = m_json->get(); m_arrayPosCache[pos] = std::make_unique(&a.at(pos)); } return *m_arrayPosCache[pos]; } std::vector> array() override { std::vector> vec; if(m_json->is()) { picojson::array &a = m_json->get(); for (auto &item : a) { picojson::value *ptr = &item; vec.emplace_back(std::make_unique(ptr)); } } return vec; } inline std::vector> &array(std::string_view key) override { if(m_arrayListDataCache.count(key.data()) == 0) { if(count(key.data()) > 0) { if (isObject()) { picojson::object &obj = m_json->get(); picojson::value &v = obj.at(key.data()); bool isArray = v.is(); if (isArray) { picojson::array &a = v.get(); std::for_each(a.begin(), a.end(), [&](picojson::value &item) { picojson::value *ptr = &item; m_arrayListDataCache[key.data()].emplace_back(std::make_unique(ptr)); }); } } } } return m_arrayListDataCache[key.data()]; } [[nodiscard]] inline size_t size() const override { if (m_json->is()) { picojson::object obj = m_json->get(); return obj.size(); } return 0; } inline bool parse(const fs::path &path) override { clearCache(); m_data = nullptr; m_json = nullptr; if (fs::exists(path) && fs::is_regular_file(path)) { m_data = std::make_unique(); std::ifstream i(path.u8string()); try { std::string error = picojson::parse(*m_data, i); if(!error.empty()) { std::cerr << "PicoJson parse error: " << error << "\n"; return false; } //i >> *m_data; m_json = m_data.get(); } catch (const std::exception &error) { std::string message = "Parse error: "; message += std::string(error.what()); message += std::string("\n"); std::cerr << message; return false; } return true; } return false; } inline bool parse(const void *data, size_t size) override { clearCache(); m_json = nullptr; m_data = std::make_unique(); tson::MemoryStream mem{(uint8_t *) data, size}; try { std::string error = picojson::parse(*m_data, mem); if(!error.empty()) { std::cerr << "PicoJson parse error: " << error << "\n"; return false; } //mem >> *m_data; m_json = m_data.get(); } catch (const std::exception &error) { std::string message = "Parse error: "; message += std::string(error.what()); message += std::string("\n"); std::cerr << message; return false; } return true; } [[nodiscard]] inline size_t count(std::string_view key) const override { if (isObject()) { picojson::object obj = m_json->get(); return obj.count(key.data()); } return m_json->contains(key.data()) ? 1 : 0; } [[nodiscard]] inline bool any(std::string_view key) const override { return count(key) > 0; } [[nodiscard]] inline bool isArray() const override { return m_json->is(); } [[nodiscard]] inline bool isObject() const override { return m_json->is(); } [[nodiscard]] inline bool isNull() const override { return m_json->is(); } protected: [[nodiscard]] inline int32_t getInt32(std::string_view key) override { picojson::object obj = m_json->get(); return static_cast(getDouble(key)); } [[nodiscard]] inline uint32_t getUInt32(std::string_view key) override { picojson::object obj = m_json->get(); return static_cast(getDouble(key)); } [[nodiscard]] inline int64_t getInt64(std::string_view key) override { picojson::object obj = m_json->get(); return static_cast(getDouble(key)); } [[nodiscard]] inline uint64_t getUInt64(std::string_view key) override { picojson::object obj = m_json->get(); return static_cast(getDouble(key)); } [[nodiscard]] inline double getDouble(std::string_view key) override { picojson::object obj = m_json->get(); return obj[key.data()].get(); } [[nodiscard]] inline std::string getString(std::string_view key) override { picojson::object obj = m_json->get(); return obj[key.data()].get(); } [[nodiscard]] inline bool getBool(std::string_view key) override { picojson::object obj = m_json->get(); return obj[key.data()].get(); } [[nodiscard]] float getFloat(std::string_view key) override { picojson::object obj = m_json->get(); return static_cast(getDouble(key)); } [[nodiscard]] inline int32_t getInt32() override { return static_cast(getDouble()); } [[nodiscard]] inline uint32_t getUInt32() override { return static_cast(getDouble()); } [[nodiscard]] inline int64_t getInt64() override { return static_cast(getDouble()); } [[nodiscard]] inline uint64_t getUInt64() override { return static_cast(getDouble()); } [[nodiscard]] inline double getDouble() override { return m_json->get(); } [[nodiscard]] inline std::string getString() override { return m_json->get(); } [[nodiscard]] inline bool getBool() override { return m_json->get(); } [[nodiscard]] float getFloat() override { return static_cast(getDouble()); } private: inline void clearCache() { m_arrayCache.clear(); m_arrayPosCache.clear(); m_arrayListDataCache.clear(); } picojson::value *m_json = nullptr; std::unique_ptr m_data = nullptr; //Only used if this is the owner json! //Cache! std::map> m_arrayCache; std::map> m_arrayPosCache; std::map>> m_arrayListDataCache; }; } #endif //TILESON_PICOJSON_HPP #endif /*** End of inlined file: PicoJson.hpp ***/ //#include "../json/Gason.hpp" //Unsupported /*** Start of inlined file: Json11.hpp ***/ // // Created by robin on 16.01.2021. // #ifndef TILESON_JSON11_HPP #define TILESON_JSON11_HPP namespace tson { class Json11 : public tson::IJson { public: inline Json11() = default; IJson &operator[](std::string_view key) override { if(m_arrayCache.count(key.data()) == 0) { if(m_json->is_object()) { m_arrayCache[key.data()] = std::make_unique(m_json->operator[](key.data())); } } return *m_arrayCache[key.data()].get(); } inline explicit Json11(const json11::Json &json) : m_json {&json} { } inline IJson& at(std::string_view key) override { if(m_arrayCache.count(key.data()) == 0) { if(m_json->is_object()) { m_arrayCache[key.data()] = std::make_unique(m_json->operator[](key.data())); } } return *m_arrayCache[key.data()].get(); } inline IJson& at(size_t pos) override { if(m_arrayPosCache.count(pos) == 0) { const std::vector &a = m_json->array_items(); m_arrayPosCache[pos] = std::make_unique(a.at(pos)); } return *m_arrayPosCache[pos]; } std::vector> array() override { std::vector> vec; if(m_json->is_array()) { for (const json11::Json &item : m_json->array_items()) { vec.emplace_back(std::make_unique(item)); } } return vec; } inline std::vector> &array(std::string_view key) override { if(m_arrayListDataCache.count(key.data()) == 0) { if(count(key.data()) > 0) { if(isObject()) { const json11::Json &v = m_json->operator[](key.data()); if(v.is_array()) { for (const json11::Json &item : v.array_items()) { m_arrayListDataCache[key.data()].emplace_back(std::make_unique(item)); } } } } } return m_arrayListDataCache[key.data()]; } [[nodiscard]] inline size_t size() const override { if(m_json->is_object()) return m_json->object_items().size(); else if(m_json->is_array()) return m_json->array_items().size(); return 0; } inline bool parse(const fs::path &path) override { clearCache(); m_data = nullptr; m_json = nullptr; if (fs::exists(path) && fs::is_regular_file(path)) { std::ifstream file(path.u8string()); std::string str; file.seekg(0, std::ios::end); str.reserve(file.tellg()); file.seekg(0, std::ios::beg); str.assign((std::istreambuf_iterator(file)), std::istreambuf_iterator()); m_data = std::make_unique(); try { std::string strError; *m_data = json11::Json::parse(str, strError); if(!strError.empty()) { std::cerr << strError << "\n"; return false; } m_json = m_data.get(); } catch (const std::exception &error) { std::string message = "Json11 parse error: "; message += std::string(error.what()); message += std::string("\n"); std::cerr << message; return false; } return true; } return false; } inline bool parse(const void *data, size_t size) override { clearCache(); m_json = nullptr; std::string str; str.reserve(size); tson::MemoryStream mem{(uint8_t *) data, size}; str.assign((std::istreambuf_iterator(mem)), std::istreambuf_iterator()); m_data = std::make_unique(); try { std::string strError; *m_data = json11::Json::parse(str, strError); if(!strError.empty()) { std::cout << strError << "\n"; return false; } m_json = m_data.get(); } catch (const std::exception &error) { std::string message = "Json11 parse error: "; message += std::string(error.what()); message += std::string("\n"); std::cerr << message; return false; } return true; } [[nodiscard]] inline size_t count(std::string_view key) const override { if (isObject()) { //const json11::Json &j = m_json->operator[](key.data()); //size_t s1 = j.object_items().size(); return m_json->object_items().count(key.data()); } return 0; } [[nodiscard]] inline bool any(std::string_view key) const override { return count(key) > 0; } [[nodiscard]] inline bool isArray() const override { return m_json->is_array(); } [[nodiscard]] inline bool isObject() const override { return m_json->is_object(); } [[nodiscard]] inline bool isNull() const override { return m_json->is_null(); } protected: [[nodiscard]] inline int32_t getInt32(std::string_view key) override { return static_cast(getDouble(key)); } [[nodiscard]] inline uint32_t getUInt32(std::string_view key) override { return static_cast(getDouble(key)); } [[nodiscard]] inline int64_t getInt64(std::string_view key) override { return static_cast(getDouble(key)); } [[nodiscard]] inline uint64_t getUInt64(std::string_view key) override { return static_cast(getDouble(key)); } [[nodiscard]] inline double getDouble(std::string_view key) override { return m_json->operator[](key.data()).number_value(); } [[nodiscard]] inline std::string getString(std::string_view key) override { return m_json->operator[](key.data()).string_value(); // .get(); } [[nodiscard]] inline bool getBool(std::string_view key) override { return m_json->operator[](key.data()).bool_value(); } [[nodiscard]] float getFloat(std::string_view key) override { return static_cast(getDouble(key)); } [[nodiscard]] inline int32_t getInt32() override { return static_cast(getDouble()); } [[nodiscard]] inline uint32_t getUInt32() override { return static_cast(getDouble()); } [[nodiscard]] inline int64_t getInt64() override { return static_cast(getDouble()); } [[nodiscard]] inline uint64_t getUInt64() override { return static_cast(getDouble()); } [[nodiscard]] inline double getDouble() override { return m_json->number_value(); } [[nodiscard]] inline std::string getString() override { return m_json->string_value(); } [[nodiscard]] inline bool getBool() override { return m_json->bool_value(); } [[nodiscard]] float getFloat() override { return static_cast(getDouble()); } private: inline void clearCache() { m_arrayCache.clear(); m_arrayPosCache.clear(); m_arrayListDataCache.clear(); } //Owner values char *m_endptr; std::unique_ptr m_data = nullptr; //Only used if this is the owner json! const json11::Json *m_json = nullptr; //Cache! std::map> m_arrayCache; std::map> m_arrayPosCache; std::map>> m_arrayListDataCache; }; } #endif //TILESON_JSON11_HPP /*** End of inlined file: Json11.hpp ***/ /*** Start of inlined file: Layer.hpp ***/ // // Created by robin on 22.03.2020. // #ifndef TILESON_LAYER_HPP #define TILESON_LAYER_HPP #include //#include "../external/json.hpp" /*** Start of inlined file: Chunk.hpp ***/ // // Created by robin on 22.03.2020. // #ifndef TILESON_CHUNK_HPP #define TILESON_CHUNK_HPP //#include "../external/json.hpp" namespace tson { class Chunk { public: inline Chunk() = default; inline explicit Chunk(IJson &json); inline bool parse(IJson &json); [[nodiscard]] inline const std::vector &getData() const; [[nodiscard]] inline const std::string &getBase64Data() const; [[nodiscard]] inline const Vector2i &getSize() const; [[nodiscard]] inline const Vector2i &getPosition() const; private: std::vector m_data; /*! 'data' (when uint array): Array of unsigned int (GIDs) or base64-encoded data. tilelayer only. */ std::string m_base64Data; /*! 'data' (when string): Array of unsigned int (GIDs) or base64-encoded data. */ tson::Vector2i m_size; /*! x='width' (in tiles) and y='height' (in tiles): */ tson::Vector2i m_position; /*! 'x' and 'y' position in tiles */ }; } #endif //TILESON_CHUNK_HPP /*! * Parses 'chunk' data from Tiled json and stores the values in this class * @param json json-data */ tson::Chunk::Chunk(IJson &json) { parse(json); } /*! * Parses 'chunk' data from Tiled json and stores the values in this class * @param json json-data * @return true if all mandatory fields was found. false otherwise. */ bool tson::Chunk::parse(IJson &json) { bool allFound = true; if(json.count("width") > 0 && json.count("height") > 0) m_size = {json["width"].get(), json["height"].get()}; else allFound = false; if(json.count("x") > 0 && json.count("y") > 0) m_position = {json["x"].get(), json["y"].get()}; else allFound = false; //Handle DATA (Optional) if(json.count("data") > 0) { if(json["data"].isArray()) { auto &data = json.array("data"); std::for_each(data.begin(), data.end(), [&](std::unique_ptr &item) { m_data.push_back(item->get()); }); } else m_base64Data = json["data"].get(); } return allFound; } /*! * 'data' (when uint array): Array of unsigned int (GIDs) or base64-encoded data. tilelayer only. * @return list of tile ids */ const std::vector &tson::Chunk::getData() const { return m_data; } /*! * 'data' (when string): Array of unsigned int (GIDs) or base64-encoded data. * @return base64 string */ const std::string &tson::Chunk::getBase64Data() const { return m_base64Data; } /*! * x='width' (in tiles) and y='height' (in tiles). * @return Size (x and y), containing the values from the fields 'width' and 'height' in Tiled */ const tson::Vector2i &tson::Chunk::getSize() const { return m_size; } /*! * 'x' and 'y' position in tiles * @return Position in int */ const tson::Vector2i &tson::Chunk::getPosition() const { return m_position; } /*** End of inlined file: Chunk.hpp ***/ /*** Start of inlined file: Object.hpp ***/ // // Created by robin on 22.03.2020. // #ifndef TILESON_OBJECT_HPP #define TILESON_OBJECT_HPP //#include "../external/json.hpp" /*** Start of inlined file: PropertyCollection.hpp ***/ // // Created by robin on 22.03.2020. // #ifndef TILESON_PROPERTYCOLLECTION_HPP #define TILESON_PROPERTYCOLLECTION_HPP /*** Start of inlined file: Property.hpp ***/ // // Created by robin on 22.03.2020. // #ifndef TILESON_PROPERTY_HPP #define TILESON_PROPERTY_HPP //#include "../../TilesonConfig.h" //#if USE_CPP17_FILESYSTEM #include #include /*** Start of inlined file: Enums.hpp ***/ // // Created by robin on 22.03.2020. // #ifndef TILESON_ENUMS_HPP #define TILESON_ENUMS_HPP #include /*** Start of inlined file: EnumBitflags.hpp ***/ // // Created by robin on 08.11.2020. // #ifndef TILESON_ENUMBITFLAGS_HPP #define TILESON_ENUMBITFLAGS_HPP #include #include namespace tson { #define ENABLE_BITMASK_OPERATORS(x) \ template<> \ struct EnableBitMaskOperators \ { \ static const bool enable = true; \ }; template struct EnableBitMaskOperators { static const bool enable = false; }; template typename std::enable_if::enable, Enum>::type operator |(Enum lhs, Enum rhs) { static_assert(std::is_enum::value, "template parameter is not an enum type"); using underlying = typename std::underlying_type::type; return static_cast ( static_cast(lhs) | static_cast(rhs) ); } //Permissions operator &(Permissions lhs, Permissions rhs) template typename std::enable_if::enable, Enum>::type operator &(Enum lhs, Enum rhs) { static_assert(std::is_enum::value, "template parameter is not an enum type"); using underlying = typename std::underlying_type::type; return static_cast ( static_cast(lhs) & static_cast(rhs) ); } //Permissions operator ^(Permissions lhs, Permissions rhs) template typename std::enable_if::enable, Enum>::type operator ^(Enum lhs, Enum rhs) { static_assert(std::is_enum::value, "template parameter is not an enum type"); using underlying = typename std::underlying_type::type; return static_cast ( static_cast(lhs) ^ static_cast(rhs) ); } //Permissions operator ~(Permissions rhs) template typename std::enable_if::enable, Enum>::type operator ~(Enum rhs) { static_assert(std::is_enum::value, "template parameter is not an enum type"); using underlying = typename std::underlying_type::type; return static_cast ( ~static_cast(rhs) ); } //Permissions& operator |=(Permissions &lhs, Permissions rhs) template typename std::enable_if::enable, Enum>::type &operator |=(Enum &lhs, Enum rhs) { static_assert(std::is_enum::value, "template parameter is not an enum type"); using underlying = typename std::underlying_type::type; lhs = static_cast ( static_cast(lhs) | static_cast(rhs) ); return lhs; } //Permissions& operator &=(Permissions &lhs, Permissions rhs) template typename std::enable_if::enable, Enum>::type &operator &=(Enum &lhs, Enum rhs) { static_assert(std::is_enum::value, "template parameter is not an enum type"); using underlying = typename std::underlying_type::type; lhs = static_cast ( static_cast(lhs) & static_cast(rhs) ); return lhs; } //Permissions& operator ^=(Permissions &lhs, Permissions rhs) template typename std::enable_if::enable, Enum>::type &operator ^=(Enum &lhs, Enum rhs) { static_assert(std::is_enum::value, "template parameter is not an enum type"); using underlying = typename std::underlying_type::type; lhs = static_cast ( static_cast(lhs) ^ static_cast(rhs) ); return lhs; } } #endif //TILESON_ENUMBITFLAGS_HPP /*** End of inlined file: EnumBitflags.hpp ***/ namespace tson { /*! * Type used in Property.hpp */ enum class Type : uint8_t { Undefined = 0, Color = 1, /*! color */ File = 2, /*! file */ Int = 3, /*! int */ Boolean = 4, /*! bool */ Float = 5, /*! float */ String = 6 /*! string */ }; /*! * Layer.hpp - LayerType * //'type': tilelayer, objectgroup, imagelayer or group */ enum class LayerType : uint8_t { Undefined = 0, TileLayer = 1, ObjectGroup = 2, ImageLayer = 3, Group = 4 }; /*! * Map.hpp - ParseStatus */ enum class ParseStatus : uint8_t { OK = 0, //OK unless otherwise stated FileNotFound = 1, ParseError = 2, MissingData = 3, DecompressionError = 4 }; /*! * Object.hpp - ObjectType */ enum class ObjectType : uint8_t { Undefined = 0, Object = 1, Ellipse = 2, Rectangle = 3, Point = 4, Polygon = 5, Polyline = 6, Text = 7, Template = 8 }; static constexpr uint32_t FLIPPED_HORIZONTALLY_FLAG = 0x80000000; static constexpr uint32_t FLIPPED_VERTICALLY_FLAG = 0x40000000; static constexpr uint32_t FLIPPED_DIAGONALLY_FLAG = 0x20000000; /*! * Object.hpp - ObjectFlipFlags */ enum class TileFlipFlags : uint32_t { None = 0, Diagonally = FLIPPED_DIAGONALLY_FLAG, Vertically = FLIPPED_VERTICALLY_FLAG, Horizontally = FLIPPED_HORIZONTALLY_FLAG }; /*! * Tileset.hpp - ObjectAlignment */ enum class ObjectAlignment : uint8_t { Unspecified = 0, //unspecified TopLeft = 1, //topleft Top = 2, //top TopRight = 3, //topright Left = 4, //left Center = 5, //center Right = 6, //right BottomLeft = 7, //bottomleft Bottom = 8, //bottom BottomRight = 9 //bottomright }; ENABLE_BITMASK_OPERATORS(TileFlipFlags) } #endif //TILESON_ENUMS_HPP /*** End of inlined file: Enums.hpp ***/ //#include "../external/json.hpp" namespace tson { class Property { public: //enum class Type : uint8_t //{ // Undefined = 0, // Color = 1, /*! color */ // File = 2, /*! file */ // Int = 3, /*! int */ // Boolean = 4, /*! bool */ // Float = 5, /*! float */ // String = 6 /*! string */ //}; inline Property(); inline Property(IJson &json); inline Property(std::string name, std::any value, Type type); inline void setValue(const std::any &value); inline void setStrValue(const std::string &value); inline void setName(const std::string &name); [[nodiscard]] inline const std::type_info& getValueType() const; inline std::string getValueTypeInfo(); [[nodiscard]]inline const std::any &getValue() const; template inline T getValue() const; [[nodiscard]] inline const std::string &getName() const; [[nodiscard]] inline Type getType() const; protected: inline void setTypeByString(const std::string &str); inline void setValueByType(IJson &json); Type m_type = Type::Undefined; std::string m_name; std::any m_value; //Using std::any to assign any type }; template T Property::getValue() const { bool isCorrectType = (m_value.type() == typeid(T)); if(isCorrectType) { T value = std::any_cast(m_value); return value; } else { static T defaultValue; return defaultValue; } } } tson::Property::Property() : m_name {"unnamed"} { } tson::Property::Property(IJson &json) { setTypeByString(json["type"].get()); setValueByType(json["value"]); m_name = json["name"].get(); } tson::Property::Property(std::string name, std::any value, Type type) : m_name { move(name) }, m_value { move(value) }, m_type {type} { } void tson::Property::setValue(const std::any &value) { m_value = value; } /*! * Sets the value specifically as string. * When not specified as std::string, the default is that the value will be set as char * when adding a value like "test" * This function is to make sure the value is added as string. * @param value */ void tson::Property::setStrValue(const std::string &value) { m_value = value; } const std::any &tson::Property::getValue() const { return m_value; } void tson::Property::setName(const std::string &name) { m_name = name; } const std::string &tson::Property::getName() const { return m_name; } /*! * Gets the value type as std::value_info. * This can easily be compared to types like this: * Check if int: getValueType() == typeid(int) * @return */ const std::type_info &tson::Property::getValueType() const { return m_value.type(); } /*! * Gets the value type as std::string * Examples of known types: * "i" = int * "f" = float * "b" = bool * @return */ std::string tson::Property::getValueTypeInfo() { return m_value.type().name(); } tson::Type tson::Property::getType() const { return m_type; } void tson::Property::setTypeByString(const std::string &str) { if(str == "color") m_type = tson::Type::Color; else if(str == "file") m_type = tson::Type::File; else if(str == "int") m_type = tson::Type::Int; else if(str == "bool") m_type = tson::Type::Boolean; else if(str == "float") m_type = tson::Type::Float; else if(str == "string") m_type = tson::Type::String; else m_type = tson::Type::Undefined; } void tson::Property::setValueByType(IJson &json) { switch(m_type) { case Type::Color: m_value = Colori(json.get()); break; case Type::File: m_value = fs::path(json.get()); break; case Type::Int: m_value = json.get(); break; case Type::Boolean: m_value = json.get(); break; case Type::Float: m_value = json.get(); break; case Type::String: setStrValue(json.get()); break; default: setStrValue(json.get()); break; } } #endif //TILESON_PROPERTY_HPP /*** End of inlined file: Property.hpp ***/ //#include "../external/json.hpp" #include namespace tson { class PropertyCollection { public: inline PropertyCollection() = default; inline explicit PropertyCollection(std::string id); inline tson::Property * add(const tson::Property &property); inline tson::Property * add(IJson &json); inline tson::Property * add(const std::string &name, const std::any &value, tson::Type type); inline void remove(const std::string &name); inline void setValue(const std::string &name, const std::any &value); inline void setId(const std::string &id); inline bool hasProperty(const std::string &name); inline tson::Property * getProperty(const std::string &name); inline std::map &getProperties(); inline std::vector get(); template inline T getValue(const std::string &name); [[nodiscard]] inline const std::string &getId() const; [[nodiscard]] inline size_t getSize() const; protected: std::string m_id; std::map m_properties; }; } template T tson::PropertyCollection::getValue(const std::string &name) { static T defaultT; return (m_properties.count(name) > 0) ? m_properties[name].getValue() : defaultT; } tson::PropertyCollection::PropertyCollection(std::string id) : m_id {std::move(id)} { } tson::Property *tson::PropertyCollection::add(const tson::Property &property) { m_properties[property.getName()] = property; return &m_properties[property.getName()]; } tson::Property *tson::PropertyCollection::add(IJson &json) { tson::Property property = tson::Property(json); std::string name = property.getName(); m_properties[name] = std::move(property); return &m_properties[name]; } tson::Property *tson::PropertyCollection::add(const std::string &name, const std::any &value, tson::Type type) { m_properties[name] = {name, value, type}; return &m_properties[name]; } void tson::PropertyCollection::remove(const std::string &name) { m_properties.erase(name); } /*! * Sets a value IF the property already exists. Does nothing otherwise. * See add() for adding new properties * @param name * @param value */ void tson::PropertyCollection::setValue(const std::string &name, const std::any &value) { if(m_properties.count(name) > 0) m_properties[name].setValue(value); } void tson::PropertyCollection::setId(const std::string &id) { m_id = id; } bool tson::PropertyCollection::hasProperty(const std::string &name) { return m_properties.count(name) > 0; } tson::Property *tson::PropertyCollection::getProperty(const std::string &name) { return (m_properties.count(name) > 0) ? &m_properties[name] : nullptr; } std::map &tson::PropertyCollection::getProperties() { return m_properties; } /*! * Gets vector of pointers to all the existing properties * @return */ std::vector tson::PropertyCollection::get() { std::vector props; for(auto &i : m_properties) props.emplace_back(&i.second); return props; } const std::string &tson::PropertyCollection::getId() const { return m_id; } size_t tson::PropertyCollection::getSize() const { return m_properties.size(); } #endif //TILESON_PROPERTYCOLLECTION_HPP /*** End of inlined file: PropertyCollection.hpp ***/ /*** Start of inlined file: Text.hpp ***/ // // Created by robin on 05.08.2019. // #ifndef TILESON_TEXT_HPP #define TILESON_TEXT_HPP #include namespace tson { class Text { public: inline Text() = default; /*! * * @param _text Text * @param _wrap If the text is marked as wrapped */ inline Text(std::string _text, bool _wrap, tson::Colori _color) : text {std::move(_text)}, wrap {_wrap}, color {_color} {}; //Just make it simple std::string text; tson::Colori color; bool wrap{}; }; } #endif //TILESON_TEXT_HPP /*** End of inlined file: Text.hpp ***/ namespace tson { class Object { public: //enum class Type : uint8_t //{ // Undefined = 0, // Object = 1, // Ellipse = 2, // Rectangle = 3, // Point = 4, // Polygon = 5, // Polyline = 6, // Text = 7, // Template = 8 //}; inline Object() = default; inline explicit Object(IJson &json); inline bool parse(IJson &json); [[nodiscard]] inline ObjectType getObjectType() const; [[nodiscard]] inline bool isEllipse() const; [[nodiscard]] inline uint32_t getGid() const; [[nodiscard]] inline const Vector2i &getSize() const; [[nodiscard]] inline int getId() const; [[nodiscard]] inline const std::string &getName() const; [[nodiscard]] inline bool isPoint() const; [[nodiscard]] inline float getRotation() const; [[nodiscard]] inline const std::string &getTemplate() const; [[nodiscard]] inline const std::string &getType() const; [[nodiscard]] inline bool isVisible() const; [[nodiscard]] inline const Vector2i &getPosition() const; [[nodiscard]] inline const std::vector &getPolygons() const; [[nodiscard]] inline const std::vector &getPolylines() const; [[nodiscard]] inline PropertyCollection &getProperties(); [[nodiscard]] inline const Text &getText() const; template inline T get(const std::string &name); inline tson::Property * getProp(const std::string &name); //v1.2.0-stuff [[nodiscard]] inline TileFlipFlags getFlipFlags() const; inline bool hasFlipFlags(TileFlipFlags flags); private: inline void setObjectTypeByJson(IJson &json); ObjectType m_objectType = ObjectType::Undefined; /*! Says with object type this is */ bool m_ellipse {}; /*! 'ellipse': Used to mark an object as an ellipse */ uint32_t m_gid {}; /*! 'gid': GID, only if object comes from a Tilemap */ tson::Vector2i m_size; /*! x = 'width' (Width in pixels), y = 'height' (Height in pixels). Ignored if using a gid.)*/ int m_id{}; /*! 'id': Incremental id - unique across all objects */ std::string m_name; /*! 'name': String assigned to name field in editor*/ bool m_point {}; /*! 'point': Used to mark an object as a point */ std::vector m_polygon; /*! 'polygon': A list of x,y coordinates in pixels */ std::vector m_polyline; /*! 'polyline': A list of x,y coordinates in pixels */ tson::PropertyCollection m_properties; /*! 'properties': A list of properties (name, value, type). */ float m_rotation {}; /*! 'rotation': Angle in degrees clockwise */ std::string m_template; /*! 'template': Reference to a template file, in case object is a template instance */ tson::Text m_text; /*! first: 'text' second: 'wrap' */ std::string m_type; /*! 'type': String assigned to type field in editor */ bool m_visible {}; /*! 'visible': Whether object is shown in editor. */ tson::Vector2i m_position; /*! 'x' and 'y': coordinate in pixels */ //v1.2.0-stuff tson::TileFlipFlags m_flipFlags = TileFlipFlags::None; /*! Resolved using bit 32, 31 and 30 from gid */ }; /*! * A shortcut for getting a property. Alternative to getProperties().getValue("") * @tparam T The template value * @param name Name of the property * @return The actual value, if it exists. Otherwise: The default value of the type. */ template T tson::Object::get(const std::string &name) { return m_properties.getValue(name); } } /*! * Parses a json Tiled object * @param json */ tson::Object::Object(IJson &json) { parse(json); } /*! * Parses a json Tiled object and autoamtically determines the object type based on the data presented. * Call getObjectType() to see what object type it is. * @param json * @return true if all mandatory fields was found. false otherwise. */ bool tson::Object::parse(IJson &json) { bool allFound = true; if(json.count("ellipse") > 0) m_ellipse = json["ellipse"].get(); //Optional if(json.count("gid") > 0) { uint32_t gid = json["gid"].get(); //Optional if (gid & FLIPPED_HORIZONTALLY_FLAG) m_flipFlags |= TileFlipFlags::Horizontally; if (gid & FLIPPED_VERTICALLY_FLAG) m_flipFlags |= TileFlipFlags::Vertically; if (gid & FLIPPED_DIAGONALLY_FLAG) m_flipFlags |= TileFlipFlags::Diagonally; // Clear flags gid &= ~(FLIPPED_HORIZONTALLY_FLAG | FLIPPED_VERTICALLY_FLAG | FLIPPED_DIAGONALLY_FLAG); m_gid = gid; } if(json.count("id") > 0) m_id = json["id"].get(); else allFound = false; if(json.count("name") > 0) m_name = json["name"].get(); else allFound = false; if(json.count("point") > 0) m_point = json["point"].get(); //Optional if(json.count("rotation") > 0) m_rotation = json["rotation"].get(); else allFound = false; if(json.count("template") > 0) m_template = json["template"].get(); //Optional if(json.count("type") > 0) m_type = json["type"].get(); else allFound = false; if(json.count("visible") > 0) m_visible = json["visible"].get(); else allFound = false; if(json.count("width") > 0 && json.count("height") > 0) m_size = {json["width"].get(), json["height"].get()}; else allFound = false; if(json.count("x") > 0 && json.count("y") > 0) m_position = {json["x"].get(), json["y"].get()}; else allFound = false; if(json.count("text") > 0) { bool hasColor = json["text"].count("color") > 0; tson::Color c = (hasColor) ? tson::Colori(json["text"]["color"].get()) : tson::Colori(); m_text = {json["text"]["text"].get(), json["text"]["wrap"].get(), c}; //Optional } setObjectTypeByJson(json); if(m_objectType == ObjectType::Template) allFound = true; //Just accept anything with this type //More advanced data if(json.count("polygon") > 0 && json["polygon"].isArray()) { auto &polygon = json.array("polygon"); std::for_each(polygon.begin(), polygon.end(),[&](std::unique_ptr &item) { IJson &j = *item; m_polygon.emplace_back(j["x"].get(), j["y"].get()); }); } if(json.count("polyline") > 0 && json["polyline"].isArray()) { auto &polyline = json.array("polyline"); std::for_each(polyline.begin(), polyline.end(),[&](std::unique_ptr &item) { IJson &j = *item; m_polyline.emplace_back(j["x"].get(), j["y"].get()); }); } if(json.count("properties") > 0 && json["properties"].isArray()) { auto &properties = json.array("properties"); std::for_each(properties.begin(), properties.end(), [&](std::unique_ptr &item) { m_properties.add(*item); }); } return allFound; } /*! * Sets an object type based on json data. * @param json */ void tson::Object::setObjectTypeByJson(IJson &json) { m_objectType = ObjectType::Undefined; if(m_ellipse) m_objectType = ObjectType::Ellipse; else if(m_point) m_objectType = ObjectType::Point; else if(json.count("polygon") > 0) m_objectType = ObjectType::Polygon; else if(json.count("polyline") > 0) m_objectType = ObjectType::Polyline; else if(json.count("text") > 0) m_objectType = ObjectType::Text; else if(json.count("gid") > 0) m_objectType = ObjectType::Object; else if(json.count("template") > 0) m_objectType = ObjectType::Template; else m_objectType = ObjectType::Rectangle; } /*! * Gets what type of object this is. * @return */ tson::ObjectType tson::Object::getObjectType() const { return m_objectType; } /*! * 'ellipse': Used to mark an object as an ellipse * @return */ bool tson::Object::isEllipse() const { return m_ellipse; } /*! * 'gid': GID, only if object comes from a Tilemap * @return */ uint32_t tson::Object::getGid() const { return m_gid; } /*! * x = 'width' (Width in pixels), y = 'height' (Height in pixels). Ignored if using a gid.) * @return */ const tson::Vector2i &tson::Object::getSize() const { return m_size; } /*! * 'id': Incremental id - unique across all objects * @return */ int tson::Object::getId() const { return m_id; } /*! * 'name': String assigned to name field in editor * @return */ const std::string &tson::Object::getName() const { return m_name; } /*! * 'point': Used to mark an object as a point * @return true if the object is of type point */ bool tson::Object::isPoint() const { return m_point; } /*! * 'rotation': Angle in degrees clockwise * @return */ float tson::Object::getRotation() const { return m_rotation; } /*! * 'template': Reference to a template file, in case object is a template instance * @return */ const std::string &tson::Object::getTemplate() const { return m_template; } /*! * 'type': String assigned to type field in editor * @return */ const std::string &tson::Object::getType() const { return m_type; } /*! * 'visible': Whether object is shown in editor. * @return */ bool tson::Object::isVisible() const { return m_visible; } /*! * 'x' and 'y': coordinate in pixels * @return */ const tson::Vector2i &tson::Object::getPosition() const { return m_position; } /*! * 'polygon': A list of x,y coordinates in pixels. * If this is a Polygon type, this function will return the points used to create it * @return */ const std::vector &tson::Object::getPolygons() const { return m_polygon; } /*! * 'polyline': A list of x,y coordinates in pixels * If this is a Polyline type, this function will return the points used to create it * @return */ const std::vector &tson::Object::getPolylines() const { return m_polyline; } /*! * 'properties': A list of properties (name, value, type). * @return */ tson::PropertyCollection &tson::Object::getProperties() { return m_properties; } /*! * 'type': String assigned to type field in editor * @return */ const tson::Text &tson::Object::getText() const { return m_text; } /*! * Shortcut for getting a property object. Alternative to getProperties().getProperty(""); * @param name Name of the property * @return */ tson::Property *tson::Object::getProp(const std::string &name) { if(m_properties.hasProperty(name)) return m_properties.getProperty(name); return nullptr; } /*! * Get all flip flags * @return */ tson::TileFlipFlags tson::Object::getFlipFlags() const { return m_flipFlags; } /*! * * @param flags Which flags to check for. Several flags can be checked at once using the bitwise or operator. * Example: * hasFlipFlags(TileFlipFlags::Vertically | TileFlipFlags::Horizontally) * * @return true if the flag(s) specified are set */ bool tson::Object::hasFlipFlags(TileFlipFlags flags) { return ((m_flipFlags & flags) == flags) ? true : false; } #endif //TILESON_OBJECT_HPP /*** End of inlined file: Object.hpp ***/ /*** Start of inlined file: TileObject.hpp ***/ // // Created by robin on 26.07.2020. // #ifndef TILESON_TILEOBJECT_HPP #define TILESON_TILEOBJECT_HPP /*** Start of inlined file: Rect.hpp ***/ // // Created by robin on 24.07.2020. // #ifndef TILESON_RECT_HPP #define TILESON_RECT_HPP namespace tson { class Rect { public: inline Rect(); inline Rect(int x_, int y_, int width_, int height_); inline bool operator==(const Rect &rhs) const; inline bool operator!=(const Rect &rhs) const; int x; int y; int width; int height; }; Rect::Rect() { } Rect::Rect(int x_, int y_, int width_, int height_) { x = x_; y = y_; width = width_; height = height_; } bool Rect::operator==(const Rect &rhs) const { return x == rhs.x && y == rhs.y && width == rhs.width && height == rhs.height; } bool Rect::operator!=(const Rect &rhs) const { return !(rhs == *this); } } #endif //TILESON_RECT_HPP /*** End of inlined file: Rect.hpp ***/ namespace tson { class Tile; class TileObject { public: inline TileObject() = default; inline TileObject(const std::tuple &posInTileUnits, tson::Tile *tile); inline void initialize(const std::tuple &posInTileUnits, tson::Tile *tile); //Defined in tileson_forward.hpp inline Tile *getTile() const; inline const Vector2i &getPositionInTileUnits() const; inline const Vector2f &getPosition() const; inline const tson::Rect &getDrawingRect() const; //Defined in tileson_forward.hpp private: tson::Tile *m_tile; tson::Vector2i m_posInTileUnits; tson::Vector2f m_position; }; TileObject::TileObject(const std::tuple &posInTileUnits, tson::Tile *tile) { initialize(posInTileUnits, tile); } /*! * Get a pointer to the related tile * @return */ Tile *TileObject::getTile() const { return m_tile; } /*! * Gets the position of the tile in tile units * @return */ const Vector2i &TileObject::getPositionInTileUnits() const { return m_posInTileUnits; } /*! * Gets the position of the tile in pixels. * @return */ const Vector2f &TileObject::getPosition() const { return m_position; } } #endif //TILESON_TILEOBJECT_HPP /*** End of inlined file: TileObject.hpp ***/ /*** Start of inlined file: FlaggedTile.hpp ***/ // // Created by robin on 13.11.2020. // #ifndef TILESON_FLAGGEDTILE_HPP #define TILESON_FLAGGEDTILE_HPP namespace tson { class FlaggedTile { public: FlaggedTile(size_t x_, size_t y_, uint32_t id_, uint32_t tileId_) : x {x_}, y {y_}, id {id_}, tileId {tileId_} { } size_t x; size_t y; /*! Full ID, including flag */ uint32_t id; /*! ID of the flagged tile */ uint32_t tileId; }; } #endif //TILESON_FLAGGEDTILE_HPP /*** End of inlined file: FlaggedTile.hpp ***/ namespace tson { class Tile; class Map; class Layer { public: inline Layer() = default; inline Layer(IJson &json, tson::Map *map); inline bool parse(IJson &json, tson::Map *map); [[nodiscard]] inline const std::string &getCompression() const; [[nodiscard]] inline const std::vector &getData() const; [[nodiscard]] inline const std::string &getBase64Data() const; [[nodiscard]] inline const std::string &getDrawOrder() const; [[nodiscard]] inline const std::string &getEncoding() const; [[nodiscard]] inline int getId() const; [[nodiscard]] inline const std::string &getImage() const; [[nodiscard]] inline const std::string &getName() const; [[nodiscard]] inline const Vector2f &getOffset() const; [[nodiscard]] inline float getOpacity() const; [[nodiscard]] inline const Vector2i &getSize() const; [[nodiscard]] inline const Colori &getTransparentcolor() const; [[nodiscard]] inline LayerType getType() const; [[nodiscard]] inline const std::string &getTypeStr() const; [[nodiscard]] inline bool isVisible() const; [[nodiscard]] inline int getX() const; [[nodiscard]] inline int getY() const; [[nodiscard]] inline std::vector &getChunks(); [[nodiscard]] inline std::vector &getLayers(); [[nodiscard]] inline std::vector &getObjects(); [[nodiscard]] inline PropertyCollection &getProperties(); inline tson::Object *getObj(int id); inline tson::Object *firstObj(const std::string &name); inline std::vector getObjectsByName(const std::string &name); inline std::vector getObjectsByType(tson::ObjectType type); template inline T get(const std::string &name); inline tson::Property * getProp(const std::string &name); inline void assignTileMap(std::map *tileMap); inline void createTileData(const Vector2i &mapSize, bool isInfiniteMap); [[nodiscard]] inline const std::map, tson::Tile *> &getTileData() const; inline tson::Tile * getTileData(int x, int y); //v1.2.0-stuff [[nodiscard]] inline const Colori &getTintColor() const; [[nodiscard]] inline tson::Map *getMap() const; [[nodiscard]] inline const std::map, tson::TileObject> &getTileObjects() const; inline tson::TileObject * getTileObject(int x, int y); [[nodiscard]] inline const std::set &getUniqueFlaggedTiles() const; inline void resolveFlaggedTiles(); private: inline void setTypeByString(); std::vector m_chunks; /*! 'chunks': Array of chunks (optional). tilelayer only. */ std::string m_compression; /*! 'compression': zlib, gzip or empty (default). tilelayer only. */ std::vector m_data; /*! 'data' (when uint array): Array of unsigned int (GIDs) or base64-encoded * data. tilelayer only. */ std::string m_base64Data; /*! 'data' (when string): Array of unsigned int (GIDs) or base64-encoded * data. tilelayer only. */ std::string m_drawOrder; /*! 'draworder': topdown (default) or index. objectgroup only. */ std::string m_encoding; /*! 'encoding': csv (default) or base64. tilelayer only. */ int m_id{}; /*! 'id': Incremental id - unique across all layers */ std::string m_image; /*! 'image': Image used by this layer. imagelayer only. */ std::vector m_layers; /*! 'layers': Array of layers. group on */ std::string m_name; /*! 'name': Name assigned to this layer */ std::vector m_objects; /*! 'objects': Array of objects. objectgroup only. */ tson::Vector2f m_offset; /*! 'offsetx' and 'offsety': Horizontal and Vertical layer offset in pixels * (default: {0, 0}) */ float m_opacity{}; /*! 'opacity': Value between 0 and 1 */ tson::PropertyCollection m_properties; /*! 'properties': A list of properties (name, value, type). */ tson::Vector2i m_size; /*! x = 'width': (Column count. Same as map width for fixed-size maps.) y = 'height': Row count. Same as map height for fixed-size maps. */ tson::Colori m_transparentcolor; /*! 'transparentcolor': Hex-formatted color (#RRGGBB) (optional, imagelayer only */ std::string m_typeStr; /*! 'type': tilelayer, objectgroup, imagelayer or group */ LayerType m_type {LayerType::Undefined}; /*! Layer type as enum*/ bool m_visible{}; /*! 'visible': Whether layer is shown or hidden in editor */ int m_x{}; /*! 'x': Horizontal layer offset in tiles. Always 0. */ int m_y{}; /*! 'y': Vertical layer offset in tiles. Always 0. */ std::map *m_tileMap; std::map, tson::Tile*> m_tileData; /*! Key: Tuple of x and y pos in tile units. */ //v1.2.0-stuff tson::Colori m_tintcolor; /*! 'tintcolor': Hex-formatted color (#RRGGBB or #AARRGGBB) that is multiplied with * any graphics drawn by this layer or any child layers (optional). */ inline void decompressData(); /*! Defined in tileson_forward.hpp */ inline void queueFlaggedTile(size_t x, size_t y, uint32_t id); /*! Queue a flagged tile */ tson::Map * m_map; /*! The map who owns this layer */ std::map, tson::TileObject> m_tileObjects; std::set m_uniqueFlaggedTiles; std::vector m_flaggedTiles; }; /*! * A shortcut for getting a property. Alternative to getProperties().getValue("") * @tparam T The template value * @param name Name of the property * @return The actual value, if it exists. Otherwise: The default value of the type. */ template T Layer::get(const std::string &name) { return m_properties.getValue(name); } } /*! * Parses a Tiled layer from json * @param json */ tson::Layer::Layer(IJson &json, tson::Map *map) { parse(json, map); } void tson::Layer::queueFlaggedTile(size_t x, size_t y, uint32_t id) { uint32_t tileId = id; tileId &= ~(FLIPPED_HORIZONTALLY_FLAG | FLIPPED_VERTICALLY_FLAG | FLIPPED_DIAGONALLY_FLAG); m_uniqueFlaggedTiles.insert(id); m_flaggedTiles.emplace_back(x, y, id, tileId); } /*! * Parses a Tiled layer from json * @param json * @return true if all mandatory fields was found. false otherwise. */ bool tson::Layer::parse(IJson &json, tson::Map *map) { m_map = map; bool allFound = true; if(json.count("tintcolor") > 0) m_tintcolor = tson::Colori(json["tintcolor"].get()); //Optional if(json.count("compression") > 0) m_compression = json["compression"].get(); //Optional if(json.count("draworder") > 0) m_drawOrder = json["draworder"].get(); //Optional if(json.count("encoding") > 0) m_encoding = json["encoding"].get(); //Optional if(json.count("id") > 0) m_id = json["id"].get(); //Optional if(json.count("image") > 0) m_image = json["image"].get(); //Optional if(json.count("name") > 0) m_name = json["name"].get(); else allFound = false; if(json.count("offsetx") > 0 && json.count("offsety") > 0) m_offset = {json["offsetx"].get(), json["offsety"].get()}; //Optional if(json.count("opacity") > 0) m_opacity = json["opacity"].get(); else allFound = false; if(json.count("width") > 0 && json.count("height") > 0) m_size = {json["width"].get(), json["height"].get()}; //else allFound = false; - Not mandatory for all layers! if(json.count("transparentcolor") > 0) m_transparentcolor = tson::Colori(json["transparentcolor"].get()); //Optional if(json.count("type") > 0) m_typeStr = json["type"].get(); else allFound = false; if(json.count("visible") > 0) m_visible = json["visible"].get(); else allFound = false; if(json.count("x") > 0) m_x = json["x"].get(); else allFound = false; if(json.count("y") > 0) m_y = json["y"].get(); else allFound = false; //Handle DATA (Optional) if(json.count("data") > 0) { if(json["data"].isArray()) { auto &array = json.array("data"); std::for_each(array.begin(), array.end(), [&](std::unique_ptr &item) { m_data.push_back(item->get()); }); } else { m_base64Data = json["data"].get(); decompressData(); } } //More advanced data if(json.count("chunks") > 0 && json["chunks"].isArray()) { auto &chunks = json.array("chunks"); std::for_each(chunks.begin(), chunks.end(), [&](std::unique_ptr &item) { m_chunks.emplace_back(*item); }); } if(json.count("layers") > 0 && json["layers"].isArray()) { auto &layers = json.array("layers"); std::for_each(layers.begin(), layers.end(), [&](std::unique_ptr &item) { m_layers.emplace_back(*item, m_map); }); } if(json.count("objects") > 0 && json["objects"].isArray()) { auto &objects = json.array("objects"); std::for_each(objects.begin(), objects.end(), [&](std::unique_ptr &item) { m_objects.emplace_back(*item); }); } if(json.count("properties") > 0 && json["properties"].isArray()) { auto &properties = json.array("properties"); std::for_each(properties.begin(), properties.end(), [&](std::unique_ptr &item) { m_properties.add(*item); }); } setTypeByString(); return allFound; } /*! * Copies all objects with a name that equals the parameter * @param name Name of the objects to return * @return All objects with a matching name */ std::vector tson::Layer::getObjectsByName(const std::string &name) { std::vector found; std::copy_if(m_objects.begin(), m_objects.end(), std::back_inserter(found), [&](const tson::Object &item) { return item.getName() == name; }); return found; } /*! * Copies all objects with a type that equals the parameter * @param type LayerType of the objects to return * @return All objects with a matching type */ std::vector tson::Layer::getObjectsByType(tson::ObjectType type) { std::vector found; std::copy_if(m_objects.begin(), m_objects.end(), std::back_inserter(found), [&](const tson::Object &item) { return item.getObjectType() == type; }); return found; } /*! * Returns the first object with the given name * @param name Name of the object to find. * @return A pointer to the object if found. nullptr otherwise. */ tson::Object *tson::Layer::firstObj(const std::string &name) { auto result = std::find_if(m_objects.begin(), m_objects.end(), [&](const tson::Object &obj){return obj.getName() == name; }); if(result == m_objects.end()) return nullptr; return &result.operator*(); } /*! * Get an object by ID * @param id Unique ID of the object * @return A pointer to the object if found. nullptr otherwise. */ tson::Object *tson::Layer::getObj(int id) { auto result = std::find_if(m_objects.begin(), m_objects.end(), [&](const tson::Object &obj){return obj.getId() == id; }); if(result == m_objects.end()) return nullptr; return &result.operator*(); } /*! * Set type by string * tilelayer, objectgroup, imagelayer or group */ void tson::Layer::setTypeByString() { if(m_typeStr == "tilelayer") m_type = LayerType::TileLayer; else if(m_typeStr == "objectgroup") m_type = LayerType::ObjectGroup; else if(m_typeStr == "imagelayer") m_type = LayerType::ImageLayer; else if(m_typeStr == "group") m_type = LayerType::Group; else m_type = LayerType::Undefined; } /*! * 'compression': zlib, gzip or empty (default). tilelayer only. * @return */ const std::string &tson::Layer::getCompression() const { return m_compression; } /*! * 'data' (when uint array): Array of unsigned int (GIDs) or base64-encoded data. tilelayer only. * @return */ const std::vector &tson::Layer::getData() const { return m_data; } /*! * 'data' (when string): Array of unsigned int (GIDs) or base64-encoded data. tilelayer only. * @return */ const std::string &tson::Layer::getBase64Data() const { return m_base64Data; } /*! * 'draworder': topdown (default) or index. objectgroup only. * @return */ const std::string &tson::Layer::getDrawOrder() const { return m_drawOrder; } /*! * 'encoding': csv (default) or base64. tilelayer only. * @return */ const std::string &tson::Layer::getEncoding() const { return m_encoding; } /*! * 'id': Incremental id - unique across all layers * @return */ int tson::Layer::getId() const { return m_id; } /*! * 'image': Image used by this layer. imagelayer only. * @return */ const std::string &tson::Layer::getImage() const { return m_image; } /*! * 'name': Name assigned to this layer * @return */ const std::string &tson::Layer::getName() const { return m_name; } /*! * 'offsetx' and 'offsety': Horizontal and Vertical layer offset in pixels (default: {0, 0}) * @return */ const tson::Vector2f &tson::Layer::getOffset() const { return m_offset; } /*! * 'opacity': Value between 0 and 1 * @return */ float tson::Layer::getOpacity() const { return m_opacity; } /*! * x = 'width': (Column count. Same as map width for fixed-size maps.) * y = 'height': Row count. Same as map height for fixed-size maps. * @return width and height as a single size */ const tson::Vector2i &tson::Layer::getSize() const { return m_size; } /*! * 'transparentcolor': Color created from a hex color (#RRGGBB) (optional, imagelayer only) * @return color as color object with rgba channel. */ const tson::Colori &tson::Layer::getTransparentcolor() const { return m_transparentcolor; } /*! * 'type': tilelayer, objectgroup, imagelayer or group * @return string with the object type */ const std::string &tson::Layer::getTypeStr() const { return m_typeStr; } /*! * 'visible': Whether layer is shown or hidden in editor * @return */ bool tson::Layer::isVisible() const { return m_visible; } /*! * 'x': Horizontal layer offset in tiles. Always 0. * @return x value (always 0 for layer) */ int tson::Layer::getX() const { return m_x; } /*! * 'y': Horizontal layer offset in tiles. Always 0. * @return y value (always 0 for layer) */ int tson::Layer::getY() const { return m_y; } /*! * 'chunks': Array of chunks (optional). tilelayer only. * @return */ std::vector &tson::Layer::getChunks() { return m_chunks; } /*! * 'layers': Array of layers. group on * @return */ std::vector &tson::Layer::getLayers() { return m_layers; } /*! * 'objects': Array of objects. objectgroup only. * @return */ std::vector &tson::Layer::getObjects() { return m_objects; } /*! * 'properties': A list of properties (name, value, type). * @return */ tson::PropertyCollection &tson::Layer::getProperties() { return m_properties; } /*! * Shortcut for getting a property object. Alternative to getProperties().getProperty(""); * @param name Name of the property * @return */ tson::Property *tson::Layer::getProp(const std::string &name) { if(m_properties.hasProperty(name)) return m_properties.getProperty(name); return nullptr; } /*! * Get layer type * @return Layer type as enum */ tson::LayerType tson::Layer::getType() const { return m_type; } /*! * Assigns a tilemap of pointers to existing tiles. * @param tileMap The tilemap. key: tile id, value: pointer to Tile. */ void tson::Layer::assignTileMap(std::map *tileMap) { m_tileMap = tileMap; } /*! * Get tile data as some kind of map with x and y position with pointers to existing tiles. * Map only contains tiles that are not empty. x and y position is in tile units. * * Example of getting tile from the returned map: * * Tile *tile = tileData[{0, 4}]; * * @return A map that represents the data returned from getData() in a 2D map with Tile pointers. */ const std::map, tson::Tile *> &tson::Layer::getTileData() const { return m_tileData; } /*! * A safe way to get tile data * Get tile data as some kind of map with x and y position with pointers to existing tiles. * Map only contains tiles that are not empty. x and y position is in tile units. * * Example of getting tile: * Tile *tile = layer->getTileData(0, 4) * * @param x X position in tile units * @param y Y position in tile units * @return pointer to tile, if it exists. nullptr otherwise. */ tson::Tile *tson::Layer::getTileData(int x, int y) { return (m_tileData.count({x, y}) > 0) ? m_tileData[{x,y}] : nullptr; } /*! * Used for getting the tson::Map who is the parent of this Layer. * @return a pointer to the tson::Map where this layer is contained. */ tson::Map *tson::Layer::getMap() const { return m_map; } /*! * * This is only supported for non-infinite maps! * * @param mapSize The size of the map * @param isInfiniteMap Whether or not the current map is infinte. */ void tson::Layer::createTileData(const Vector2i &mapSize, bool isInfiniteMap) { size_t x = 0; size_t y = 0; if(!isInfiniteMap) { std::for_each(m_data.begin(), m_data.end(), [&](uint32_t tileId) { if (x == mapSize.x) { ++y; x = 0; } if (tileId > 0 && m_tileMap->count(tileId) > 0) { m_tileData[{x, y}] = m_tileMap->at(tileId); m_tileObjects[{x, y}] = {{x, y}, m_tileData[{x, y}]}; } else if(tileId > 0 && m_tileMap->count(tileId) == 0) //Tile with flip flags! { queueFlaggedTile(x, y, tileId); } x++; }); } } const std::map, tson::TileObject> &tson::Layer::getTileObjects() const { return m_tileObjects; } tson::TileObject *tson::Layer::getTileObject(int x, int y) { return (m_tileObjects.count({x, y}) > 0) ? &m_tileObjects[{x,y}] : nullptr; } const std::set &tson::Layer::getUniqueFlaggedTiles() const { return m_uniqueFlaggedTiles; } void tson::Layer::resolveFlaggedTiles() { std::for_each(m_flaggedTiles.begin(), m_flaggedTiles.end(), [&](const tson::FlaggedTile &tile) { if (tile.id > 0 && m_tileMap->count(tile.id) > 0) { m_tileData[{tile.x, tile.y}] = m_tileMap->at(tile.id); m_tileObjects[{tile.x, tile.y}] = {{tile.x, tile.y}, m_tileData[{tile.x, tile.y}]}; } }); } /*! * 'tintcolor': Hex-formatted color (#RRGGBB or #AARRGGBB) that is multiplied with any graphics drawn by this layer or any child layers (optional). * * @return tintcolor */ const tson::Colori &tson::Layer::getTintColor() const { return m_tintcolor; } #endif //TILESON_LAYER_HPP /*** End of inlined file: Layer.hpp ***/ /*** Start of inlined file: Tileset.hpp ***/ // // Created by robin on 22.03.2020. // #ifndef TILESON_TILESET_HPP #define TILESON_TILESET_HPP //#include "../external/json.hpp" /*** Start of inlined file: WangSet.hpp ***/ // // Created by robin on 22.03.2020. // #ifndef TILESON_WANGSET_HPP #define TILESON_WANGSET_HPP //#include "../external/json.hpp" /*** Start of inlined file: WangColor.hpp ***/ // // Created by robin on 22.03.2020. // #ifndef TILESON_WANGCOLOR_HPP #define TILESON_WANGCOLOR_HPP //#include "../external/json.hpp" namespace tson { class WangColor { public: inline WangColor() = default; inline explicit WangColor(IJson &json); inline bool parse(IJson &json); [[nodiscard]] inline const Colori &getColor() const; [[nodiscard]] inline const std::string &getName() const; [[nodiscard]] inline float getProbability() const; [[nodiscard]] inline int getTile() const; private: tson::Colori m_color; /*! 'color': Hex-formatted color (#RRGGBB or #AARRGGBB) */ std::string m_name; /*! 'name': Name of the Wang color */ float m_probability{}; /*! 'probability': Probability used when randomizing */ int m_tile{}; /*! 'tile': Local ID of tile representing the Wang color */ }; } tson::WangColor::WangColor(IJson &json) { parse(json); } bool tson::WangColor::parse(IJson &json) { bool allFound = true; if(json.count("color") > 0) m_color = tson::Colori(json["color"].get()); else allFound = false; if(json.count("name") > 0) m_name = json["name"].get(); else allFound = false; if(json.count("probability") > 0) m_probability = json["probability"].get(); else allFound = false; if(json.count("tile") > 0) m_tile = json["tile"].get(); else allFound = false; return allFound; } /*! * 'color': Color object created from hex-formatted string (#RRGGBB or #AARRGGBB) * @return */ const tson::Colori &tson::WangColor::getColor() const { return m_color; } /*! * 'name': Name of the Wang color * @return */ const std::string &tson::WangColor::getName() const { return m_name; } /*! * 'probability': Probability used when randomizing * @return */ float tson::WangColor::getProbability() const { return m_probability; } /*! * 'tile': Local ID of tile representing the Wang color * @return */ int tson::WangColor::getTile() const { return m_tile; } #endif //TILESON_WANGCOLOR_HPP /*** End of inlined file: WangColor.hpp ***/ /*** Start of inlined file: WangTile.hpp ***/ // // Created by robin on 22.03.2020. // #ifndef TILESON_WANGTILE_HPP #define TILESON_WANGTILE_HPP //#include "../external/json.hpp" namespace tson { class WangTile { public: inline WangTile() = default; inline explicit WangTile(IJson &json); inline bool parse(IJson &json); [[nodiscard]] inline bool hasDFlip() const; [[nodiscard]] inline bool hasHFlip() const; [[nodiscard]] inline int getTileid() const; [[nodiscard]] inline bool hasVFlip() const; [[nodiscard]] inline const std::vector &getWangIds() const; private: bool m_dflip{}; /*! 'dflip': Tile is flipped diagonally */ bool m_hflip{}; /*! 'hflip': Tile is flipped horizontally */ int m_tileid{}; /*! 'tileid': Local ID of tile */ bool m_vflip{}; /*! 'vflip': Tile is flipped vertically */ std::vector m_wangId; /*! 'wangid': Array of Wang color indexes (uchar[8])*/ }; } tson::WangTile::WangTile(IJson &json) { parse(json); } /*! * Parses a wang tile from Tiled json. * @param json A Tiled json file * @return true if all mandatory fields were found. False otherwise. */ bool tson::WangTile::parse(IJson &json) { bool allFound = true; if(json.count("dflip") > 0) m_dflip = json["dflip"].get(); else allFound = false; if(json.count("hflip") > 0) m_hflip = json["hflip"].get(); else allFound = false; if(json.count("vflip") > 0) m_vflip = json["vflip"].get(); else allFound = false; if(json.count("tileid") > 0) m_tileid = json["tileid"].get(); else allFound = false; if(json.count("wangid") > 0 && json["wangid"].isArray()) { auto &wangid = json.array("wangid"); std::for_each(wangid.begin(), wangid.end(), [&](std::unique_ptr &item) { m_wangId.emplace_back(item->get()); }); } return allFound; } /*! * 'dflip': Tile is flipped diagonally * @return */ bool tson::WangTile::hasDFlip() const { return m_dflip; } /*! * 'hflip': Tile is flipped horizontally * @return */ bool tson::WangTile::hasHFlip() const { return m_hflip; } /*! * 'tileid': Local ID of tile * @return */ int tson::WangTile::getTileid() const { return m_tileid; } /*! * 'vflip': Tile is flipped vertically * @return */ bool tson::WangTile::hasVFlip() const { return m_vflip; } /*! * 'wangid': Array of Wang color indexes (uchar[8]) * @return */ const std::vector &tson::WangTile::getWangIds() const { return m_wangId; } #endif //TILESON_WANGTILE_HPP /*** End of inlined file: WangTile.hpp ***/ namespace tson { class WangSet { public: inline WangSet() = default; inline explicit WangSet(IJson &json); inline bool parse(IJson &json); [[nodiscard]] inline const std::string &getName() const; [[nodiscard]] inline int getTile() const; [[nodiscard]] inline const std::vector &getWangTiles() const; [[nodiscard]] inline const std::vector &getCornerColors() const; [[nodiscard]] inline const std::vector &getEdgeColors() const; inline PropertyCollection &getProperties(); template inline T get(const std::string &name); inline tson::Property * getProp(const std::string &name); private: std::string m_name; /*! 'name': Name of the Wang set */ int m_tile{}; /*! 'tile': Local ID of tile representing the Wang set */ std::vector m_wangTiles; /*! 'wangtiles': Array of Wang tiles */ std::vector m_cornerColors; /*! 'cornercolors': Array of Wang colors */ std::vector m_edgeColors; /*! 'edgecolors': Array of Wang colors */ tson::PropertyCollection m_properties; /*! 'properties': A list of properties (name, value, type). */ }; /*! * A shortcut for getting a property. Alternative to getProperties().getValue("") * @tparam T The template value * @param name Name of the property * @return The actual value, if it exists. Otherwise: The default value of the type. */ template T tson::WangSet::get(const std::string &name) { return m_properties.getValue(name); } } tson::WangSet::WangSet(IJson &json) { parse(json); } bool tson::WangSet::parse(IJson &json) { bool allFound = true; if(json.count("tile") > 0) m_tile = json["tile"].get(); else allFound = false; if(json.count("name") > 0) m_name = json["name"].get(); else allFound = false; //More advanced data if(json.count("wangtiles") > 0 && json["wangtiles"].isArray()) { auto &wangtiles = json.array("wangtiles"); std::for_each(wangtiles.begin(), wangtiles.end(), [&](std::unique_ptr &item) { m_wangTiles.emplace_back(*item); }); } if(json.count("cornercolors") > 0 && json["cornercolors"].isArray()) { auto &cornercolors = json.array("cornercolors"); std::for_each(cornercolors.begin(), cornercolors.end(), [&](std::unique_ptr &item) { m_cornerColors.emplace_back(*item); }); } if(json.count("edgecolors") > 0 && json["edgecolors"].isArray()) { auto &edgecolors = json.array("edgecolors"); std::for_each(edgecolors.begin(), edgecolors.end(), [&](std::unique_ptr &item) { m_edgeColors.emplace_back(*item); }); } if(json.count("properties") > 0 && json["properties"].isArray()) { auto &properties = json.array("properties"); std::for_each(properties.begin(), properties.end(), [&](std::unique_ptr &item) { m_properties.add(*item); }); } return allFound; } /*! * 'name': Name of the Wang set * @return */ const std::string &tson::WangSet::getName() const { return m_name; } /*! * 'tile': Local ID of tile representing the Wang set * @return */ int tson::WangSet::getTile() const { return m_tile; } /*! * 'wangtiles': Array of Wang tiles * @return */ const std::vector &tson::WangSet::getWangTiles() const { return m_wangTiles; } /*! * 'cornercolors': Array of Wang colors * @return */ const std::vector &tson::WangSet::getCornerColors() const { return m_cornerColors; } /*! * 'edgecolors': Array of Wang colors * @return */ const std::vector &tson::WangSet::getEdgeColors() const { return m_edgeColors; } /*! * 'properties': A list of properties (name, value, type). * @return */ tson::PropertyCollection &tson::WangSet::getProperties() { return m_properties; } /*! * Shortcut for getting a property object. Alternative to getProperties().getProperty(""); * @param name Name of the property * @return */ tson::Property *tson::WangSet::getProp(const std::string &name) { if(m_properties.hasProperty(name)) return m_properties.getProperty(name); return nullptr; } #endif //TILESON_WANGSET_HPP /*** End of inlined file: WangSet.hpp ***/ /*** Start of inlined file: Tile.hpp ***/ // // Created by robin on 22.03.2020. // #ifndef TILESON_TILE_HPP #define TILESON_TILE_HPP //#include "../external/json.hpp" /*** Start of inlined file: Frame.hpp ***/ // // Created by robin on 22.03.2020. // #ifndef TILESON_FRAME_HPP #define TILESON_FRAME_HPP //#include "../external/json.hpp" namespace tson { class Frame { public: inline Frame() = default; inline Frame(int duration, int tileId); inline explicit Frame(IJson &json); inline bool parse(IJson &json); [[nodiscard]] inline int getDuration() const; [[nodiscard]] inline int getTileId() const; private: int m_duration {}; /*! 'duration': Frame duration in milliseconds */ int m_tileId {}; /*! 'tileid': Local tile ID representing this frame */ }; } /*! * * @param duration duration in milliseconds * @param tileId TileId */ tson::Frame::Frame(int duration, int tileId) : m_duration {duration}, m_tileId {tileId} { } /*! * Parses frame data from json * @param json */ tson::Frame::Frame(IJson &json) { parse(json); } /*! * Parses frame data from json * @param json * @return true if all mandatory fields was found. false otherwise. */ bool tson::Frame::parse(IJson &json) { bool allFound = true; if(json.count("duration") > 0) m_duration = json["duration"].get(); else allFound = false; if(json.count("tileid") > 0) m_tileId = json["tileid"].get(); else allFound = false; return allFound; } /*! * 'duration': Frame duration in milliseconds * @return Duration in milliseconds */ int tson::Frame::getDuration() const { return m_duration; } /*! * 'tileid': Local tile ID representing this frame * @return tile id */ int tson::Frame::getTileId() const { return m_tileId; } #endif //TILESON_FRAME_HPP /*** End of inlined file: Frame.hpp ***/ namespace tson { class Tileset; class Tile { public: inline Tile() = default; inline Tile(IJson &json, tson::Tileset *tileset, tson::Map *map); inline Tile(uint32_t id, tson::Tileset *tileset, tson::Map *map); inline Tile(uint32_t id, tson::Map *map); //v1.2.0 inline bool parse(IJson &json, tson::Tileset *tileset, tson::Map *map); inline bool parseId(IJson &json); [[nodiscard]] inline uint32_t getId() const; [[nodiscard]] inline const fs::path &getImage() const; [[nodiscard]] inline const Vector2i &getImageSize() const; [[nodiscard]] inline const std::string &getType() const; [[nodiscard]] inline const std::vector &getAnimation() const; [[nodiscard]] inline const Layer &getObjectgroup() const; [[nodiscard]] inline PropertyCollection &getProperties(); [[nodiscard]] inline const std::vector &getTerrain() const; template inline T get(const std::string &name); inline tson::Property * getProp(const std::string &name); //v1.2.0-stuff inline void setProperties(const tson::PropertyCollection &properties); inline tson::Tileset * getTileset() const; inline tson::Map * getMap() const; inline const tson::Rect &getDrawingRect() const; inline const tson::Vector2f getPosition(const std::tuple &tileDataPos); inline const tson::Vector2i getPositionInTileUnits(const std::tuple &tileDataPos); inline const tson::Vector2i getTileSize() const; /*! Declared in tileson_forward.hpp */ [[nodiscard]] inline TileFlipFlags getFlipFlags() const; inline bool hasFlipFlags(TileFlipFlags flags); [[nodiscard]] inline uint32_t getGid() const; inline void addTilesetAndPerformCalculations(tson::Tileset *tileset); //v1.2.0 private: std::vector m_animation; /*! 'animation': Array of Frames */ uint32_t m_id {}; /*! 'id': Local ID of the tile */ fs::path m_image; /*! 'image': Image representing this tile (optional)*/ tson::Vector2i m_imageSize; /*! x = 'imagewidth' and y = 'imageheight': in pixels */ tson::Layer m_objectgroup; /*! 'objectgroup': Layer with type objectgroup (optional) */ tson::PropertyCollection m_properties; /*! 'properties': A list of properties (name, value, type). */ std::vector m_terrain; /*! 'terrain': Index of terrain for each corner of tile */ std::string m_type; /*! 'type': The type of the tile (optional) */ //v1.2.0-stuff uint32_t m_gid {}; /*! id without flip flags */ tson::Tileset * m_tileset; /*! A pointer to the tileset where this Tile comes from */ tson::Map * m_map; /*! A pointer to the map where this tile is contained */ tson::Rect m_drawingRect; /*! A rect that shows which part of the tileset that is used for this tile */ tson::TileFlipFlags m_flipFlags = TileFlipFlags::None; /*! Resolved using bit 32, 31 and 30 from gid */ inline void performDataCalculations(); /*! Declared in tileson_forward.hpp - Calculate all the values used in the tile class. */ inline void manageFlipFlagsByIdThenRemoveFlags(uint32_t &id); friend class Layer; }; /*! * A shortcut for getting a property. Alternative to getProperties().getValue("") * @tparam T The template value * @param name Name of the property * @return The actual value, if it exists. Otherwise: The default value of the type. */ template T tson::Tile::get(const std::string &name) { return m_properties.getValue(name); } } tson::Tile::Tile(IJson &json, tson::Tileset *tileset, tson::Map *map) { parse(json, tileset, map); } /*! * Used in cases where you have a tile without any property * @param id */ tson::Tile::Tile(uint32_t id, tson::Tileset *tileset, tson::Map *map) : m_id {id}, m_gid {id} { m_tileset = tileset; m_map = map; manageFlipFlagsByIdThenRemoveFlags(m_gid); performDataCalculations(); } /*! * Used in cases where you have a FLIP FLAGGED tile * @param id */ tson::Tile::Tile(uint32_t id, tson::Map *map) : m_id {id}, m_gid {id} { m_map = map; manageFlipFlagsByIdThenRemoveFlags(m_gid); } /*! * For flip flagged tiles, tilesets must be resolved later. * @param tileset */ void tson::Tile::addTilesetAndPerformCalculations(tson::Tileset *tileset) { m_tileset = tileset; performDataCalculations(); } /*! * Parses a tile from a Tiled json. id on tile is store as id + 1 to match the references in data containers. * @param json * @return */ bool tson::Tile::parse(IJson &json, tson::Tileset *tileset, tson::Map *map) { m_tileset = tileset; m_map = map; if(json.count("image") > 0) m_image = fs::path(json["image"].get()); //Optional bool allFound = parseId(json); if(json.count("type") > 0) m_type = json["type"].get(); //Optional if(json.count("objectgroup") > 0) m_objectgroup = tson::Layer(json["objectgroup"], m_map); //Optional if(json.count("imagewidth") > 0 && json.count("imageheight") > 0) m_imageSize = {json["imagewidth"].get(), json["imageheight"].get()}; //Optional //More advanced data if(json.count("animation") > 0 && json["animation"].isArray()) { auto &animation = json.array("animation"); std::for_each(animation.begin(), animation.end(), [&](std::unique_ptr &item) { m_animation.emplace_back(*item); }); } if(json.count("terrain") > 0 && json["terrain"].isArray()) { auto &terrain = json.array("terrain"); std::for_each(terrain.begin(), terrain.end(), [&](std::unique_ptr &item) { m_terrain.emplace_back(item->get()); }); } if(json.count("properties") > 0 && json["properties"].isArray()) { auto &properties = json.array("properties"); std::for_each(properties.begin(), properties.end(), [&](std::unique_ptr &item) { m_properties.add(*item); }); } performDataCalculations(); return allFound; } /*! * 'id': Local ID of the tile * @return */ uint32_t tson::Tile::getId() const { return m_id; } /*! * 'image': Image representing this tile (optional) * @return */ const fs::path &tson::Tile::getImage() const { return m_image; } /*! * x = 'imagewidth' and y = 'imageheight': in pixels * @return */ const tson::Vector2i &tson::Tile::getImageSize() const { return m_imageSize; } /*! * 'type': The type of the tile (optional) * @return */ const std::string &tson::Tile::getType() const { return m_type; } /*! * 'animation': Array of Frames * @return */ const std::vector &tson::Tile::getAnimation() const { return m_animation; } /*! * 'objectgroup': Layer with type objectgroup (optional) * @return */ const tson::Layer &tson::Tile::getObjectgroup() const { return m_objectgroup; } /*! * 'properties': A list of properties (name, value, type). * @return */ tson::PropertyCollection &tson::Tile::getProperties() { return m_properties; } /*! * 'terrain': Index of terrain for each corner of tile * @return */ const std::vector &tson::Tile::getTerrain() const { return m_terrain; } /*! * Shortcut for getting a property object. Alternative to getProperties().getProperty(""); * @param name Name of the property * @return */ tson::Property *tson::Tile::getProp(const std::string &name) { if(m_properties.hasProperty(name)) return m_properties.getProperty(name); return nullptr; } /*! * Used for getting the tson::Tileset who is the parent of this Tile. * @return a pointer to the tson::Tileset where this tile is contained. */ tson::Tileset *tson::Tile::getTileset() const { return m_tileset; } /*! * Used for getting the tson::Map who is the parent of this Tile. * @return a pointer to the tson::Map where this tile is contained. */ tson::Map *tson::Tile::getMap() const { return m_map; } /*! * Get the information needed to draw the Tile based on its current tileset * @return a tson::Rect containing the information needed to draw the tile. */ const tson::Rect &tson::Tile::getDrawingRect() const { return m_drawingRect; } /*! * Helper function. * * Get the position of the tile in tile units. * The size of each unit is determined by the tile size property of the map. * Example: If the tile size is 16x16 in the map, a tile unit of [2, 4] would be [32, 64] in pixels. * If you want the position in pixels: use getPosition() instead. * * @return Position of tile in tile units. */ const tson::Vector2i tson::Tile::getPositionInTileUnits(const std::tuple &tileDataPos) { return {std::get<0>(tileDataPos), std::get<1>(tileDataPos)}; } void tson::Tile::manageFlipFlagsByIdThenRemoveFlags(uint32_t &id) { if (id & FLIPPED_HORIZONTALLY_FLAG) m_flipFlags |= TileFlipFlags::Horizontally; if (id & FLIPPED_VERTICALLY_FLAG) m_flipFlags |= TileFlipFlags::Vertically; if (id & FLIPPED_DIAGONALLY_FLAG) m_flipFlags |= TileFlipFlags::Diagonally; id &= ~(FLIPPED_HORIZONTALLY_FLAG | FLIPPED_VERTICALLY_FLAG | FLIPPED_DIAGONALLY_FLAG); } tson::TileFlipFlags tson::Tile::getFlipFlags() const { return m_flipFlags; } /*! * * @param flags Which flags to check for. Several flags can be checked at once using the bitwise or operator. * Example: * hasFlipFlags(TileFlipFlags::Vertically | TileFlipFlags::Horizontally) * * @return true if the flag(s) specified are set */ bool tson::Tile::hasFlipFlags(tson::TileFlipFlags flags) { return ((m_flipFlags & flags) == flags) ? true : false; } uint32_t tson::Tile::getGid() const { return m_gid; } void tson::Tile::setProperties(const tson::PropertyCollection &properties) { m_properties = properties; } #endif //TILESON_TILE_HPP /*** End of inlined file: Tile.hpp ***/ /*** Start of inlined file: Terrain.hpp ***/ // // Created by robin on 22.03.2020. // #ifndef TILESON_TERRAIN_HPP #define TILESON_TERRAIN_HPP //#include "../external/json.hpp" namespace tson { class Terrain { public: inline Terrain() = default; inline Terrain(std::string name, int tile); inline explicit Terrain(IJson &json); inline bool parse(IJson &json); [[nodiscard]] inline const std::string &getName() const; [[nodiscard]] inline int getTile() const; [[nodiscard]] inline PropertyCollection &getProperties(); template inline T get(const std::string &name); inline tson::Property * getProp(const std::string &name); private: std::string m_name; /*! 'name': Name of terrain */ int m_tile {}; /*! 'tile': Local ID of tile representing terrain */ tson::PropertyCollection m_properties; /*! 'properties': A list of properties (name, value, type). */ }; /*! * A shortcut for getting a property. Alternative to getProperties().getValue("") * @tparam T The template value * @param name Name of the property * @return The actual value, if it exists. Otherwise: The default value of the type. */ template T tson::Terrain::get(const std::string &name) { return m_properties.getValue(name); } } tson::Terrain::Terrain(std::string name, int tile) : m_name {std::move(name)}, m_tile {tile} { } tson::Terrain::Terrain(IJson &json) { parse(json); } bool tson::Terrain::parse(IJson &json) { bool allFound = true; if(json.count("name") > 0) m_name = json["name"].get(); else allFound = false; if(json.count("tile") > 0) m_tile = json["tile"].get(); else allFound = false; if(json.count("properties") > 0 && json["properties"].isArray()) { auto &properties = json.array("properties"); std::for_each(properties.begin(), properties.end(), [&](std::unique_ptr &item) { m_properties.add(*item); }); } return allFound; } /*! * 'name': Name of terrain * @return */ const std::string &tson::Terrain::getName() const { return m_name; } /*! * 'tile': Local ID of tile representing terrain * @return */ int tson::Terrain::getTile() const { return m_tile; } /*! * 'properties': A list of properties (name, value, type). *Missing from the official Tiled documentation...* * @return */ tson::PropertyCollection &tson::Terrain::getProperties() { return m_properties; } /*! * Shortcut for getting a property object. Alternative to getProperties().getProperty(""); * @param name Name of the property * @return */ tson::Property *tson::Terrain::getProp(const std::string &name) { if(m_properties.hasProperty(name)) return m_properties.getProperty(name); return nullptr; } #endif //TILESON_TERRAIN_HPP /*** End of inlined file: Terrain.hpp ***/ /*** Start of inlined file: Grid.hpp ***/ // // Created by robin on 22.03.2020. // #ifndef TILESON_GRID_HPP #define TILESON_GRID_HPP #include //#include "../external/json.hpp" namespace tson { class Grid { public: inline Grid() = default; inline explicit Grid(IJson &json); inline bool parse(IJson &json); [[nodiscard]] inline const std::string &getOrientation() const; [[nodiscard]] inline const Vector2i &getSize() const; private: std::string m_orientation; /*! 'orientation': Orientation of the grid for the tiles in this tileset (orthogonal or isometric) */ tson::Vector2i m_size; /*! 'width' and 'height': Size. */ }; } /*! * Parses Tiled grid data from json * @param json */ tson::Grid::Grid(IJson &json) { parse(json); } /*! * Parses Tiled grid data from json * @param json * @return true if all mandatory fields was found. false otherwise. */ bool tson::Grid::parse(IJson &json) { bool allFound = true; if(json.count("orientation") > 0) m_orientation = json["orientation"].get(); //Optional if(json.count("width") > 0 && json.count("height") > 0) m_size = {json["width"].get(), json["height"].get()}; else allFound = false; return allFound; } /*! * 'orientation': Orientation of the grid for the tiles in this tileset (orthogonal or isometric) * @return orientation as string */ const std::string &tson::Grid::getOrientation() const { return m_orientation; } /*! * 'width' and 'height': Size. * @return size as int */ const tson::Vector2i &tson::Grid::getSize() const { return m_size; } #endif //TILESON_GRID_HPP /*** End of inlined file: Grid.hpp ***/ #include namespace tson { class Map; class Tileset { public: inline Tileset() = default; inline explicit Tileset(IJson &json, tson::Map *map); inline bool parse(IJson &json, tson::Map *map); [[nodiscard]] inline int getColumns() const; [[nodiscard]] inline int getFirstgid() const; [[nodiscard]] inline const fs::path &getImagePath() const; [[nodiscard]] inline const fs::path &getImage() const; [[nodiscard]] inline const Vector2i &getImageSize() const; [[nodiscard]] inline int getMargin() const; [[nodiscard]] inline const std::string &getName() const; [[nodiscard]] inline int getSpacing() const; [[nodiscard]] inline int getTileCount() const; [[nodiscard]] inline const Vector2i &getTileSize() const; [[nodiscard]] inline const Colori &getTransparentColor() const; [[nodiscard]] inline const std::string &getType() const; [[nodiscard]] inline std::vector &getTiles(); [[nodiscard]] inline const std::vector &getWangsets() const; [[nodiscard]] inline PropertyCollection &getProperties(); [[nodiscard]] inline const std::vector &getTerrains() const; [[nodiscard]] inline const Vector2i &getTileOffset() const; [[nodiscard]] inline const Grid &getGrid() const; inline tson::Tile * getTile(int id); inline tson::Terrain * getTerrain(const std::string &name); template inline T get(const std::string &name); inline tson::Property * getProp(const std::string &name); //v1.2.0-stuff [[nodiscard]] inline tson::Map *getMap() const; [[nodiscard]] inline ObjectAlignment getObjectAlignment() const; inline static tson::ObjectAlignment StringToAlignment(std::string_view str); private: inline void generateMissingTiles(); int m_columns {}; /*! 'columns': The number of tile columns in the tileset */ int m_firstgid {}; /*! 'firstgid': GID corresponding to the first tile in the set */ fs::path m_image; /*! 'image': Image used for tiles in this set */ tson::Vector2i m_imageSize; /*! x = 'imagewidth' and y = 'imageheight': in pixels */ int m_margin {}; /*! 'margin': Buffer between image edge and first tile (pixels)*/ std::string m_name; /*! 'name': Name given to this tileset */ int m_spacing {}; /*! 'spacing': Spacing between adjacent tiles in image (pixels)*/ int m_tileCount {}; /*! 'tilecount': The number of tiles in this tileset */ tson::Vector2i m_tileSize; /*! x = 'tilewidth' and y = 'tileheight': Maximum size of tiles in this set */ tson::Colori m_transparentColor; /*! 'transparentcolor': Hex-formatted color (#RRGGBB) (optional) */ std::string m_type; /*! 'type': tileset (for tileset files, since 1.0) */ std::vector m_tiles; /*! 'tiles': Array of Tiles (optional) */ std::vector m_wangsets; /*! 'wangsets':Array of Wang sets (since 1.1.5) */ tson::PropertyCollection m_properties; /*! 'properties': A list of properties (name, value, type). */ std::vector m_terrains; /*! 'terrains': Array of Terrains (optional) */ tson::Vector2i m_tileOffset; /*! 'x' and 'y': See (optional) */ tson::Grid m_grid; /*! 'grid': This element is only used in case of isometric orientation, and determines how tile overlays for terrain and collision information are rendered. */ //v1.2.0-stuff tson::ObjectAlignment m_objectAlignment{tson::ObjectAlignment::Unspecified}; /*! 'objectalignment': Alignment to use for tile objects. Tiled 1.4.*/ tson::Map * m_map; /*! The map who owns this tileset */ }; /*! * A shortcut for getting a property. Alternative to getProperties().getValue("") * @tparam T The template value * @param name Name of the property * @return The actual value, if it exists. Otherwise: The default value of the type. */ template T tson::Tileset::get(const std::string &name) { return m_properties.getValue(name); } } tson::Tileset::Tileset(IJson &json, tson::Map *map) { parse(json, map); } bool tson::Tileset::parse(IJson &json, tson::Map *map) { m_map = map; bool allFound = true; if(json.count("columns") > 0) m_columns = json["columns"].get(); else allFound = false; if(json.count("firstgid") > 0) m_firstgid = json["firstgid"].get(); else allFound = false; if(json.count("image") > 0) m_image = fs::path(json["image"].get()); else allFound = false; if(json.count("margin") > 0) m_margin = json["margin"].get(); else allFound = false; if(json.count("name") > 0) m_name = json["name"].get(); else allFound = false; if(json.count("spacing") > 0) m_spacing = json["spacing"].get(); else allFound = false; if(json.count("tilecount") > 0) m_tileCount = json["tilecount"].get(); else allFound = false; if(json.count("transparentcolor") > 0) m_transparentColor = tson::Colori(json["transparentcolor"].get()); //Optional if(json.count("type") > 0) m_type = json["type"].get(); if(json.count("grid") > 0) m_grid = tson::Grid(json["grid"]); if(json.count("imagewidth") > 0 && json.count("imageheight") > 0) m_imageSize = {json["imagewidth"].get(), json["imageheight"].get()}; else allFound = false; if(json.count("tilewidth") > 0 && json.count("tileheight") > 0) m_tileSize = {json["tilewidth"].get(), json["tileheight"].get()}; else allFound = false; if(json.count("tileoffset") > 0) m_tileOffset = {json["tileoffset"]["x"].get(), json["tileoffset"]["y"].get()}; //More advanced data if(json.count("wangsets") > 0 && json["wangsets"].isArray()) { auto &wangsets = json.array("wangsets"); std::for_each(wangsets.begin(), wangsets.end(), [&](std::unique_ptr &item) { m_wangsets.emplace_back(*item); }); } if(json.count("tiles") > 0 && json["tiles"].isArray()) { auto &tiles = json.array("tiles"); std::for_each(tiles.begin(), tiles.end(), [&](std::unique_ptr &item) { m_tiles.emplace_back(*item, this, m_map); }); } if(json.count("terrains") > 0 && json["terrains"].isArray()) { auto &terrains = json.array("terrains"); std::for_each(terrains.begin(), terrains.end(), [&](std::unique_ptr &item) { m_terrains.emplace_back(*item); }); } if(json.count("properties") > 0 && json["properties"].isArray()) { auto &properties = json.array("properties"); std::for_each(properties.begin(), properties.end(), [&](std::unique_ptr &item) { m_properties.add(*item); }); } if(json.count("objectalignment") > 0) { std::string alignment = json["objectalignment"].get(); m_objectAlignment = StringToAlignment(alignment); } generateMissingTiles(); return allFound; } /*! * 'columns': The number of tile columns in the tileset * @return */ int tson::Tileset::getColumns() const { return m_columns; } /*! * 'firstgid': GID corresponding to the first tile in the set * @return */ int tson::Tileset::getFirstgid() const { return m_firstgid; } /*! * 'image': Image used for tiles in this set * @return */ const fs::path &tson::Tileset::getImagePath() const { return m_image; } /*! * x = 'imagewidth' and y = 'imageheight': in pixels * @return */ const tson::Vector2i &tson::Tileset::getImageSize() const { return m_imageSize; } /*! * 'margin': Buffer between image edge and first tile (pixels) * @return */ int tson::Tileset::getMargin() const { return m_margin; } /*! * 'name': Name given to this tileset * @return */ const std::string &tson::Tileset::getName() const { return m_name; } /*! * 'spacing': Spacing between adjacent tiles in image (pixels) * @return */ int tson::Tileset::getSpacing() const { return m_spacing; } /*! * 'tilecount': The number of tiles in this tileset * @return */ int tson::Tileset::getTileCount() const { return m_tileCount; } /*! * x = 'tilewidth' and y = 'tileheight': Maximum size of tiles in this set * @return */ const tson::Vector2i &tson::Tileset::getTileSize() const { return m_tileSize; } /*! * 'transparentcolor': Color object created by hex-formatted color (#RRGGBB) (optional) * @return */ const tson::Colori &tson::Tileset::getTransparentColor() const { return m_transparentColor; } /*! * 'type': tileset (for tileset files, since 1.0) * @return */ const std::string &tson::Tileset::getType() const { return m_type; } /*! * 'image': Image used for tiles in this set * @return */ const fs::path &tson::Tileset::getImage() const { return m_image; } /*! * 'tiles': Array of Tiles (optional) * @return */ std::vector &tson::Tileset::getTiles() { return m_tiles; } /*! * 'wangsets':Array of Wang sets (since Tiled 1.1.5) * @return */ const std::vector &tson::Tileset::getWangsets() const { return m_wangsets; } /*! * 'properties': A list of properties (name, value, type). * @return */ tson::PropertyCollection &tson::Tileset::getProperties() { return m_properties; } /*! * 'terrains': Array of Terrains (optional) * @return */ const std::vector &tson::Tileset::getTerrains() const { return m_terrains; } /*! * 'x' and 'y': See (optional) * @return */ const tson::Vector2i &tson::Tileset::getTileOffset() const { return m_tileOffset; } /*! * 'grid': This element is only used in case of isometric orientation, and determines * how tile overlays for terrain and collision information are rendered. * @return */ const tson::Grid &tson::Tileset::getGrid() const { return m_grid; } /*! * Gets a tile by ID (Tiled ID + 1) * @param id The ID of the tile stored in Tiled map + 1. Example: If ID was stored in Tiled map as 0, the corresponding value in Tileson is 1. * This is to make sure the IDs of tiles matches their references in containers. * @return A pointer to the Tile if found. nullptr otherwise. */ tson::Tile *tson::Tileset::getTile(int id) { auto result = std::find_if(m_tiles.begin(), m_tiles.end(), [&](const tson::Tile & item) { return item.getId() == id;}); if(result == m_tiles.end()) return nullptr; return &result.operator*(); } /*! * Get an existing Terrain object by name * @param name * @return A pointer to the Terrain if found. nullptr otherwise. */ tson::Terrain *tson::Tileset::getTerrain(const std::string &name) { auto result = std::find_if(m_terrains.begin(), m_terrains.end(), [&](const tson::Terrain & item) { return item.getName() == name;}); if(result == m_terrains.end()) return nullptr; return &result.operator*(); } /*! * Shortcut for getting a property object. Alternative to getProperties().getProperty(""); * @param name Name of the property * @return */ tson::Property *tson::Tileset::getProp(const std::string &name) { if(m_properties.hasProperty(name)) return m_properties.getProperty(name); return nullptr; } /*! * Tiled only has tiles with a property stored in the map. This function makes sure even the ones with no properties will exist. */ void tson::Tileset::generateMissingTiles() { std::vector tileIds; for(auto &tile : m_tiles) tileIds.push_back(tile.getId()); for(uint32_t i = m_firstgid; i < m_firstgid + m_tileCount; ++i) { if(std::count(tileIds.begin(), tileIds.end(), i) == 0) { m_tiles.emplace_back(Tile(i, this, m_map)); } } } /*! * Used for getting the tson::Map who is the parent of this Tileset. * @return a pointer to the tson::Map where this tileset is contained. */ tson::Map *tson::Tileset::getMap() const { return m_map; } /*! * * @param str The string you want to convert * @return Alignment enum based on the string from the input. */ tson::ObjectAlignment tson::Tileset::StringToAlignment(std::string_view str) { if(str == "unspecified") return tson::ObjectAlignment::Unspecified; else if(str == "topleft") return tson::ObjectAlignment::TopLeft; else if(str == "top") return tson::ObjectAlignment::Top; else if(str == "topright") return tson::ObjectAlignment::TopRight; else if(str == "left") return tson::ObjectAlignment::Left; else if(str == "center") return tson::ObjectAlignment::Center; else if(str == "right") return tson::ObjectAlignment::Right; else if(str == "bottomleft") return tson::ObjectAlignment::BottomLeft; else if(str == "bottom") return tson::ObjectAlignment::Bottom; else if(str == "bottomright") return tson::ObjectAlignment::BottomRight; else return tson::ObjectAlignment::Unspecified; } tson::ObjectAlignment tson::Tileset::getObjectAlignment() const { return m_objectAlignment; } #endif //TILESON_TILESET_HPP /*** End of inlined file: Tileset.hpp ***/ namespace tson { class Map { public: inline Map() = default; inline Map(ParseStatus status, std::string description); inline explicit Map(IJson &json, tson::DecompressorContainer *decompressors); inline bool parse(IJson &json, tson::DecompressorContainer *decompressors); [[nodiscard]] inline const Colori &getBackgroundColor() const; [[nodiscard]] inline const Vector2i &getSize() const; [[nodiscard]] inline int getHexsideLength() const; [[nodiscard]] inline bool isInfinite() const; [[nodiscard]] inline int getNextLayerId() const; [[nodiscard]] inline int getNextObjectId() const; [[nodiscard]] inline const std::string &getOrientation() const; [[nodiscard]] inline const std::string &getRenderOrder() const; [[nodiscard]] inline const std::string &getStaggerAxis() const; [[nodiscard]] inline const std::string &getStaggerIndex() const; [[nodiscard]] inline const std::string &getTiledVersion() const; [[nodiscard]] inline const Vector2i &getTileSize() const; [[nodiscard]] inline const std::string &getType() const; [[nodiscard]] inline int getVersion() const; [[nodiscard]] inline std::vector &getLayers(); [[nodiscard]] inline PropertyCollection &getProperties(); [[nodiscard]] inline std::vector &getTilesets(); [[nodiscard]] inline ParseStatus getStatus() const; [[nodiscard]] inline const std::string &getStatusMessage() const; [[nodiscard]] inline const std::map &getTileMap() const; inline Layer * getLayer(const std::string &name); inline Tileset * getTileset(const std::string &name); template inline T get(const std::string &name); inline tson::Property * getProp(const std::string &name); //v1.2.0 [[nodiscard]] inline int getCompressionLevel() const; inline DecompressorContainer *getDecompressors(); inline Tileset * getTilesetByGid(uint32_t gid); private: inline void createTilesetData(IJson &json); inline void processData(); Colori m_backgroundColor; /*! 'backgroundcolor': Hex-formatted color (#RRGGBB or #AARRGGBB) (optional)*/; Vector2i m_size; /*! 'width' and 'height' of a Tiled map */ int m_hexsideLength {}; /*! 'hexsidelength': Length of the side of a hex tile in pixels */ bool m_isInfinite {}; /*! 'infinite': Whether the map has infinite dimensions*/ std::vector m_layers; /*! 'layers': Array of layers. group on */ int m_nextLayerId {}; /*! 'nextlayerid': Auto-increments for each layer */ int m_nextObjectId {}; /*! 'nextobjectid': Auto-increments for each placed object */ std::string m_orientation; /*! 'orientation': orthogonal, isometric, staggered or hexagonal */ tson::PropertyCollection m_properties; /*! 'properties': A list of properties (name, value, type). */ std::string m_renderOrder; /*! 'renderorder': Rendering direction (orthogonal maps only) */ std::string m_staggerAxis; /*! 'staggeraxis': x or y (staggered / hexagonal maps only) */ std::string m_staggerIndex; /*! 'staggerindex': odd or even (staggered / hexagonal maps only) */ std::string m_tiledVersion; /*! 'tiledversion': The Tiled version used to save the file */ Vector2i m_tileSize; /*! 'tilewidth': and 'tileheight' of a map */ std::vector m_tilesets; /*! 'tilesets': Array of Tilesets */ std::string m_type; /*! 'type': map (since 1.0) */ int m_version{}; /*! 'version': The JSON format version*/ ParseStatus m_status {ParseStatus::OK}; std::string m_statusMessage {"OK"}; std::map m_tileMap; /*! key: Tile ID. Value: Pointer to Tile*/ //v1.2.0 int m_compressionLevel {-1}; /*! 'compressionlevel': The compression level to use for tile layer * data (defaults to -1, which means to use the algorithm default) * Introduced in Tiled 1.3*/ tson::DecompressorContainer * m_decompressors; std::map m_flaggedTileMap; /*! key: Tile ID. Value: Tile*/ }; /*! * A shortcut for getting a property. Alternative to getProperties().getValue("") * @tparam T The template value * @param name Name of the property * @return The actual value, if it exists. Otherwise: The default value of the type. */ template T tson::Map::get(const std::string &name) { return m_properties.getValue(name); } } /*! * When errors have happened before the map starts parsing, just keep the statuses * @param status The status * @param description Description of the status */ tson::Map::Map(tson::ParseStatus status, std::string description) : m_status {status}, m_statusMessage { std::move(description) } { } /*! * Parses a json of a Tiled map. * @param json A json object with the format of Map * @return true if all mandatory fields was found. false otherwise. */ tson::Map::Map(IJson &json, tson::DecompressorContainer *decompressors) { parse(json, decompressors); } /*! * Parses a json of a Tiled map. * @param json A json object with the format of Map * @return true if all mandatory fields was found. false otherwise. */ bool tson::Map::parse(IJson &json, tson::DecompressorContainer *decompressors) { m_decompressors = decompressors; bool allFound = true; if(json.count("compressionlevel") > 0) m_compressionLevel = json["compressionlevel"].get(); //Tiled 1.3 - Optional if(json.count("backgroundcolor") > 0) m_backgroundColor = Colori(json["backgroundcolor"].get()); //Optional if(json.count("width") > 0 && json.count("height") > 0 ) m_size = {json["width"].get(), json["height"].get()}; else allFound = false; if(json.count("hexsidelength") > 0) m_hexsideLength = json["hexsidelength"].get(); //Optional if(json.count("infinite") > 0) m_isInfinite = json["infinite"].get(); //Optional if(json.count("nextlayerid") > 0) m_nextLayerId = json["nextlayerid"].get(); //Optional if(json.count("nextobjectid") > 0) m_nextObjectId = json["nextobjectid"].get(); else allFound = false; if(json.count("orientation") > 0) m_orientation = json["orientation"].get(); else allFound = false; if(json.count("renderorder") > 0) m_renderOrder = json["renderorder"].get(); //Optional if(json.count("staggeraxis") > 0) m_staggerAxis = json["staggeraxis"].get(); //Optional if(json.count("staggerindex") > 0) m_staggerIndex = json["staggerindex"].get(); //Optional if(json.count("tiledversion") > 0) m_tiledVersion = json["tiledversion"].get(); else allFound = false; if(json.count("tilewidth") > 0 && json.count("tileheight") > 0 ) m_tileSize = {json["tilewidth"].get(), json["tileheight"].get()}; else allFound = false; if(json.count("type") > 0) m_type = json["type"].get(); //Optional if(json.count("version") > 0) m_version = json["version"].get(); else allFound = false; //More advanced data if(json.count("layers") > 0 && json["layers"].isArray()) { auto &array = json.array("layers"); std::for_each(array.begin(), array.end(), [&](std::unique_ptr &item) { m_layers.emplace_back(*item, this); }); } if(json.count("properties") > 0 && json["properties"].isArray()) { auto &array = json.array("properties"); std::for_each(array.begin(), array.end(), [&](std::unique_ptr &item) { m_properties.add(*item); }); } createTilesetData(json); processData(); return allFound; } /*! * Tileset data must be created in two steps to prevent malformed tson::Tileset pointers inside tson::Tile */ void tson::Map::createTilesetData(IJson &json) { if(json.count("tilesets") > 0 && json["tilesets"].isArray()) { //First created tileset objects auto &tilesets = json.array("tilesets"); std::for_each(tilesets.begin(), tilesets.end(), [&](std::unique_ptr &item) { m_tilesets.emplace_back(); }); int i = 0; //Then do the parsing std::for_each(tilesets.begin(), tilesets.end(), [&](std::unique_ptr &item) { m_tilesets[i].parse(*item, this); ++i; }); } } /*! * Processes the parsed data and uses the data to create helpful objects, like tile maps. */ void tson::Map::processData() { m_tileMap.clear(); for(auto &tileset : m_tilesets) { std::for_each(tileset.getTiles().begin(), tileset.getTiles().end(), [&](tson::Tile &tile) { m_tileMap[tile.getGid()] = &tile; }); } std::for_each(m_layers.begin(), m_layers.end(), [&](tson::Layer &layer) { layer.assignTileMap(&m_tileMap); layer.createTileData(m_size, m_isInfinite); const std::set &flaggedTiles = layer.getUniqueFlaggedTiles(); for(uint32_t ftile : flaggedTiles) { tson::Tile tile {ftile, layer.getMap()}; if(m_tileMap.count(tile.getGid())) { tson::Tile *originalTile = m_tileMap[tile.getGid()]; tile.addTilesetAndPerformCalculations(originalTile->getTileset()); tile.setProperties(originalTile->getProperties()); m_flaggedTileMap[ftile] = tile; m_tileMap[ftile] = &m_flaggedTileMap[ftile]; } } layer.resolveFlaggedTiles(); }); } /*! * 'backgroundcolor': Color created from a hex-formatted color string (#RRGGBB or #AARRGGBB) (optional) * @return string as color */ const tson::Colori &tson::Map::getBackgroundColor() const { return m_backgroundColor; } /*! * 'width' and 'height' of a Tiled map * @return */ const tson::Vector2 &tson::Map::getSize() const { return m_size; } /*! * 'hexsidelength': Length of the side of a hex tile in pixels * @return */ int tson::Map::getHexsideLength() const { return m_hexsideLength; } /*! * 'infinite': Whether the map has infinite dimensions * @return */ bool tson::Map::isInfinite() const { return m_isInfinite; } /*! * 'nextlayerid': Auto-increments for each layer * @return */ int tson::Map::getNextLayerId() const { return m_nextLayerId; } /*! * 'nextobjectid': Auto-increments for each placed object * @return */ int tson::Map::getNextObjectId() const { return m_nextObjectId; } /*! * 'orientation': orthogonal, isometric, staggered or hexagonal * @return */ const std::string &tson::Map::getOrientation() const { return m_orientation; } /*! * 'renderorder': Rendering direction (orthogonal maps only) * @return */ const std::string &tson::Map::getRenderOrder() const { return m_renderOrder; } /*! * 'staggeraxis': x or y (staggered / hexagonal maps only) * @return */ const std::string &tson::Map::getStaggerAxis() const { return m_staggerAxis; } /*! * 'staggerindex': odd or even (staggered / hexagonal maps only) * @return */ const std::string &tson::Map::getStaggerIndex() const { return m_staggerIndex; } /*! * 'tiledversion': The Tiled version used to save the file * @return */ const std::string &tson::Map::getTiledVersion() const { return m_tiledVersion; } /*! * 'tilewidth': and 'tileheight' of a map * @return */ const tson::Vector2 &tson::Map::getTileSize() const { return m_tileSize; } /*! * 'type': map (since 1.0) * @return */ const std::string &tson::Map::getType() const { return m_type; } /*! * 'version': The JSON format version * @return */ int tson::Map::getVersion() const { return m_version; } /*! * 'layers': Array of layers. group on * @return */ std::vector &tson::Map::getLayers() { return m_layers; } /*! * 'properties': A list of properties (name, value, type). * @return */ tson::PropertyCollection &tson::Map::getProperties() { return m_properties; } /*! * 'tilesets': Array of Tilesets * @return */ std::vector &tson::Map::getTilesets() { return m_tilesets; } tson::Layer *tson::Map::getLayer(const std::string &name) { auto result = std::find_if(m_layers.begin(), m_layers.end(), [&](const tson::Layer &item) { return item.getName() == name; }); if(result == m_layers.end()) return nullptr; return &result.operator*(); } /*! * Gets a tileset by name * * @param name Name of the tileset * @return tileset with the matching name */ tson::Tileset *tson::Map::getTileset(const std::string &name) { auto result = std::find_if(m_tilesets.begin(), m_tilesets.end(), [&](const tson::Tileset &item) {return item.getName() == name; }); if(result == m_tilesets.end()) return nullptr; return &result.operator*(); } /*! * Gets a tileset by gid (graphical ID of a tile). These are always unique, no matter how many tilesets you have * * @param gid Graphical ID of a tile * @return tileset related to the actual gid */ tson::Tileset *tson::Map::getTilesetByGid(uint32_t gid) { auto result = std::find_if(m_tilesets.begin(), m_tilesets.end(), [&](const tson::Tileset &tileset) { int firstId = tileset.getFirstgid(); //First tile id of the tileset int lastId = (firstId + tileset.getTileCount()) - 1; return (gid >= firstId && gid <= lastId); }); if(result == m_tilesets.end()) return nullptr; return &result.operator*(); } /*! * Shortcut for getting a property object. Alternative to getProperties().getProperty(""); * @param name Name of the property * @return */ tson::Property *tson::Map::getProp(const std::string &name) { if(m_properties.hasProperty(name)) return m_properties.getProperty(name); return nullptr; } tson::ParseStatus tson::Map::getStatus() const { return m_status; } const std::string &tson::Map::getStatusMessage() const { return m_statusMessage; } /*! * Get a tile map with pointers to every existing tile. * @return */ const std::map &tson::Map::getTileMap() const { return m_tileMap; } tson::DecompressorContainer *tson::Map::getDecompressors() { return m_decompressors; } /*! * 'compressionlevel': The compression level to use for tile layer data (defaults to -1, which means to use the algorithm default) * * @return The compression level */ int tson::Map::getCompressionLevel() const { return m_compressionLevel; } #endif //TILESON_MAP_HPP /*** End of inlined file: Map.hpp ***/ /*** Start of inlined file: Project.hpp ***/ // // Created by robin on 01.08.2020. // #ifndef TILESON_PROJECT_HPP #define TILESON_PROJECT_HPP #include #include #include /*** Start of inlined file: World.hpp ***/ // // Created by robin on 01.08.2020. // #ifndef TILESON_WORLD_HPP #define TILESON_WORLD_HPP /*** Start of inlined file: WorldMapData.hpp ***/ // // Created by robin on 01.08.2020. // #ifndef TILESON_WORLDMAPDATA_HPP #define TILESON_WORLDMAPDATA_HPP namespace tson { class WorldMapData { public: inline WorldMapData(const fs::path &folder_, IJson &json); inline void parse(const fs::path &folder_, IJson &json); //inline WorldMapData(fs::path folder_, std::string fileName_) : folder {std::move(folder_)}, fileName {fileName_} //{ // path = folder / fileName; //} fs::path folder; fs::path path; std::string fileName; tson::Vector2i size; tson::Vector2i position; }; WorldMapData::WorldMapData(const fs::path &folder_, IJson &json) { parse(folder_, json); } void WorldMapData::parse(const fs::path &folder_, IJson &json) { folder = folder_; if(json.count("fileName") > 0) fileName = json["fileName"].get(); if(json.count("height") > 0) size = {json["width"].get(), json["height"].get()}; if(json.count("x") > 0) position = {json["x"].get(), json["y"].get()}; path = (!fileName.empty()) ? folder / fileName : folder; } } #endif //TILESON_WORLDMAPDATA_HPP /*** End of inlined file: WorldMapData.hpp ***/ #include namespace tson { class Tileson; class World { public: #ifdef JSON11_IS_DEFINED inline explicit World(std::unique_ptr jsonParser = std::make_unique()) : m_json {std::move(jsonParser)} { } inline explicit World(const fs::path &path, std::unique_ptr jsonParser = std::make_unique()); #else inline explicit World(std::unique_ptr jsonParser) : m_json {std::move(jsonParser)} { } inline explicit World(const fs::path &path, std::unique_ptr jsonParser); #endif inline bool parse(const fs::path &path); inline int loadMaps(tson::Tileson *parser); //tileson_forward.hpp inline bool contains(std::string_view filename); inline const WorldMapData *get(std::string_view filename) const; [[nodiscard]] inline const fs::path &getPath() const; [[nodiscard]] inline const fs::path &getFolder() const; [[nodiscard]] inline const std::vector &getMapData() const; [[nodiscard]] inline bool onlyShowAdjacentMaps() const; [[nodiscard]] inline const std::string &getType() const; [[nodiscard]] inline const std::vector> &getMaps() const; private: inline void parseJson(IJson &json); std::unique_ptr m_json = nullptr; fs::path m_path; fs::path m_folder; std::vector m_mapData; std::vector> m_maps; bool m_onlyShowAdjacentMaps; std::string m_type; }; World::World(const fs::path &path, std::unique_ptr jsonParser) : m_json {std::move(jsonParser)} { parse(path); } bool World::parse(const fs::path &path) { m_path = path; m_folder = m_path.parent_path(); if(!m_json->parse(path)) return false; parseJson(*m_json); return true; } const fs::path &World::getPath() const { return m_path; } const std::vector &World::getMapData() const { return m_mapData; } bool World::onlyShowAdjacentMaps() const { return m_onlyShowAdjacentMaps; } const std::string &World::getType() const { return m_type; } void World::parseJson(IJson &json) { if(json.count("onlyShowAdjacentMaps") > 0) m_onlyShowAdjacentMaps = json["onlyShowAdjacentMaps"].get(); if(json.count("type") > 0) m_type = json["type"].get(); if(json["maps"].isArray()) { auto &maps = json.array("maps"); std::for_each(maps.begin(), maps.end(), [&](std::unique_ptr &item) { m_mapData.emplace_back(m_folder, *item); }); } } const fs::path &World::getFolder() const { return m_folder; } /*! * Check if there is WorldMapData in the world that contains the current filename. * Filename = . * @param filename * @return */ bool World::contains(std::string_view filename) { //Note: might be moved to std::ranges from C++20. return std::any_of(m_mapData.begin(), m_mapData.end(), [&](const auto &item) { return item.fileName == filename; }); } /*! * Get a map by its filename * @param filename Filename (including extension) - (example: file.json) * @return pointer to WorldMapData or nullptr if not exists */ const WorldMapData * World::get(std::string_view filename) const { auto iter = std::find_if(m_mapData.begin(), m_mapData.end(), [&](const auto &item) { return item.fileName == filename; }); return (iter == m_mapData.end()) ? nullptr : iter.operator->(); } /*! * Get all maps that have been loaded by loadMaps(). * NOTE: This is untested, and was a last second addition to Tileson 1.2.0, as I had forgot about the loadMaps() functionality (also untested) * If you find anything malfunctioning - please report. * @return All maps loaded by loadMaps() */ const std::vector> &World::getMaps() const { return m_maps; } } #endif //TILESON_WORLD_HPP /*** End of inlined file: World.hpp ***/ /*** Start of inlined file: ProjectFolder.hpp ***/ // // Created by robin on 01.08.2020. // #ifndef TILESON_PROJECTFOLDER_HPP #define TILESON_PROJECTFOLDER_HPP namespace tson { class ProjectFolder { public: inline ProjectFolder(const fs::path &path); inline const fs::path &getPath() const; inline bool hasWorldFile() const; inline const std::vector &getSubFolders() const; inline const std::vector &getFiles() const; inline const World &getWorld() const; private: inline void loadData(); fs::path m_path; bool m_hasWorldFile; tson::World m_world; std::vector m_subFolders; std::vector m_files; }; ProjectFolder::ProjectFolder(const fs::path &path) : m_path {path} { loadData(); } void ProjectFolder::loadData() { m_hasWorldFile = false; m_subFolders.clear(); m_files.clear(); //Search and see if there is a World file .world file fs::path worldPath; for (const auto & entry : fs::directory_iterator(m_path)) { if(fs::is_regular_file(entry.path())) { if(entry.path().extension() == ".world") { m_hasWorldFile = true; worldPath = entry.path(); } } } if(m_hasWorldFile) m_world.parse(worldPath); for (const auto & entry : fs::directory_iterator(m_path)) { if (fs::is_directory(entry.path())) m_subFolders.emplace_back(entry.path());//.loadData(); - loadData() is called in the constructor, so don't call again. else if (fs::is_regular_file(entry.path())) { if(m_hasWorldFile && m_world.contains(entry.path().filename().u8string())) m_files.emplace_back(entry.path()); else if(!m_hasWorldFile) m_files.emplace_back(entry.path()); } } } const fs::path &ProjectFolder::getPath() const { return m_path; } bool ProjectFolder::hasWorldFile() const { return m_hasWorldFile; } const std::vector &ProjectFolder::getSubFolders() const { return m_subFolders; } const std::vector &ProjectFolder::getFiles() const { return m_files; } /*! * Only gives useful data if hasWorldFile() is true! * @return */ const World &ProjectFolder::getWorld() const { return m_world; } } #endif //TILESON_PROJECTFOLDER_HPP /*** End of inlined file: ProjectFolder.hpp ***/ /*** Start of inlined file: ProjectData.hpp ***/ // // Created by robin on 01.08.2020. // #ifndef TILESON_PROJECTDATA_HPP #define TILESON_PROJECTDATA_HPP namespace tson { class ProjectData { public: ProjectData() = default; std::string automappingRulesFile; std::vector commands; std::string extensionsPath; std::vector folders; std::string objectTypesFile; //Tileson specific fs::path basePath; std::vector folderPaths; }; } #endif //TILESON_PROJECTDATA_HPP /*** End of inlined file: ProjectData.hpp ***/ namespace tson { class Project { public: #ifdef JSON11_IS_DEFINED inline explicit Project(std::unique_ptr jsonParser = std::make_unique()) : m_json {std::move(jsonParser)} { } inline explicit Project(const fs::path &path, std::unique_ptr jsonParser = std::make_unique()); #else inline explicit Project(std::unique_ptr jsonParser) : m_json {std::move(jsonParser)} { } inline explicit Project(const fs::path &path, std::unique_ptr jsonParser); #endif inline bool parse(const fs::path &path); [[nodiscard]] inline const ProjectData &getData() const; [[nodiscard]] inline const fs::path &getPath() const; [[nodiscard]] inline const std::vector &getFolders() const; private: inline void parseJson(IJson &json); fs::path m_path; std::vector m_folders; ProjectData m_data; std::unique_ptr m_json = nullptr; }; Project::Project(const fs::path &path, std::unique_ptr jsonParser) : m_json {std::move(jsonParser)} { parse(path); } bool Project::parse(const fs::path &path) { m_path = path; std::ifstream i(m_path.u8string()); try { if(!m_json->parse(path)) return false; } catch(const std::exception &error) { std::string message = "Parse error: "; message += std::string(error.what()); message += std::string("\n"); return false; } parseJson(*m_json); return true; } const ProjectData &Project::getData() const { return m_data; } void Project::parseJson(IJson &json) { m_data.basePath = m_path.parent_path(); //The directory of the project file if(json.count("automappingRulesFile") > 0) m_data.automappingRulesFile = json["automappingRulesFile"].get(); if(json.count("commands") > 0) { m_data.commands.clear(); auto &commands = json.array("commands"); std::for_each(commands.begin(), commands.end(), [&](std::unique_ptr &item) { m_data.commands.emplace_back(item->get()); }); } if(json.count("extensionsPath") > 0) m_data.extensionsPath = json["extensionsPath"].get(); if(json.count("folders") > 0) { m_data.folders.clear(); m_data.folderPaths.clear(); auto &folders = json.array("folders"); std::for_each(folders.begin(), folders.end(), [&](std::unique_ptr &item) { std::string folder = item->get(); m_data.folders.emplace_back(folder); m_data.folderPaths.emplace_back(m_data.basePath / folder); m_folders.emplace_back(m_data.basePath / folder); }); } if(json.count("objectTypesFile") > 0) m_data.objectTypesFile = json["objectTypesFile"].get(); } const fs::path &Project::getPath() const { return m_path; } const std::vector &Project::getFolders() const { return m_folders; } } #endif //TILESON_PROJECT_HPP /*** End of inlined file: Project.hpp ***/ namespace tson { class Tileson { public: #ifdef JSON11_IS_DEFINED inline explicit Tileson(std::unique_ptr jsonParser = std::make_unique(), bool includeBase64Decoder = true); #else inline explicit Tileson(std::unique_ptr jsonParser, bool includeBase64Decoder = true); #endif inline std::unique_ptr parse(const fs::path &path, std::unique_ptr, std::vector>> decompressor = nullptr); inline std::unique_ptr parse(const void * data, size_t size, std::unique_ptr, std::vector>> decompressor = nullptr); inline tson::DecompressorContainer *decompressors(); private: inline std::unique_ptr parseJson(); std::unique_ptr m_json; tson::DecompressorContainer m_decompressors; }; } /*! * * @param includeBase64Decoder Includes the base64-decoder from "Base64Decompressor.hpp" if true. * Otherwise no other decompressors/decoders than whatever the user itself have added will be used. */ tson::Tileson::Tileson(std::unique_ptr jsonParser, bool includeBase64Decoder) : m_json {std::move(jsonParser)} { if(includeBase64Decoder) m_decompressors.add(); } /*! * Parses Tiled json map data by file * @param path path to file * @return parsed data as Map */ std::unique_ptr tson::Tileson::parse(const fs::path &path, std::unique_ptr, std::vector>> decompressor) { bool result = false; if(decompressor != nullptr) { std::vector decompressed = decompressor->decompressFile(path); result = (decompressed.empty()) ? false : true; if(!result) return std::make_unique(tson::ParseStatus::DecompressionError, "Error during decompression"); result = m_json->parse(&decompressed[0], decompressed.size()); if(result) return std::move(parseJson()); } else if(m_json->parse(path)) { return std::move(parseJson()); } std::string msg = "File not found: "; msg += std::string(path.u8string()); return std::make_unique(tson::ParseStatus::FileNotFound, msg); } /*! * Parses Tiled json map data by memory * @param data The data to parse * @param size The size of the data to parse * @return parsed data as Map */ std::unique_ptr tson::Tileson::parse(const void *data, size_t size, std::unique_ptr, std::vector>> decompressor) { bool result = false; if(decompressor != nullptr) { std::vector decompressed = decompressor->decompress(data, size); result = (decompressed.empty()) ? false : true; if(!result) return std::make_unique(tson::ParseStatus::DecompressionError, "Error during decompression"); result = m_json->parse(&decompressed[0], decompressed.size()); } else result = m_json->parse(data, size); if(!result) return std::make_unique(tson::ParseStatus::ParseError, "Memory error"); return std::move(parseJson()); } /*! * Common parsing functionality for doing the json parsing * @param json Tiled json to parse * @return parsed data as Map */ std::unique_ptr tson::Tileson::parseJson() { std::unique_ptr map = std::make_unique(); if(map->parse(*m_json, &m_decompressors)) return std::move(map); return std::make_unique (tson::ParseStatus::MissingData, "Missing map data..."); } /*! * Gets the decompressor container used when something is either encoded or compressed (regardless: IDecompressor is used as base). * These are used specifically for tile layers, and are connected by checking the name of the IDecompressor. If the name of a decompressor * matches with an encoding or a compression, its decompress() function will be used. * * @return The container including all decompressors. */ tson::DecompressorContainer *tson::Tileson::decompressors() { return &m_decompressors; } #endif //TILESON_TILESON_PARSER_HPP /*** End of inlined file: tileson_parser.hpp ***/ /*** Start of inlined file: tileson_forward.hpp ***/ // // Created by robin on 25.07.2020. // #ifndef TILESON_TILESON_FORWARD_HPP #define TILESON_TILESON_FORWARD_HPP /*! * T I L E S O N F O R W A R D D E C L A R A T I O N S * ------------------------------------------------------- * * Due to cross-references we have forward declarations that cannot be resolved during the * implementation, thus the implementations must be done later when the class definition itself is known. * * All those forward declarations can be found below. */ // T i l e . h p p // --------------------- /*! * Really just a shortcut to retrieve the tile size from the map. * @return TileSize based on the map property for tile size. */ const tson::Vector2i tson::Tile::getTileSize() const { if(m_map != nullptr) return m_map->getTileSize(); else return {0,0}; } bool tson::Tile::parseId(IJson &json) { if(json.count("id") > 0) { m_id = json["id"].get() + 1; if (m_tileset != nullptr) m_gid = m_tileset->getFirstgid() + m_id - 1; else m_gid = m_id; manageFlipFlagsByIdThenRemoveFlags(m_gid); return true; } return false; } /*! * Uses tson::Tileset and tson::Map data to calculate related values for tson::Tile. * Added in v1.2.0 */ void tson::Tile::performDataCalculations() { if(m_tileset == nullptr || m_map == nullptr) return; int firstId = m_tileset->getFirstgid(); //First tile id of the tileset int columns = m_tileset->getColumns(); int rows = m_tileset->getTileCount() / columns; int lastId = (m_tileset->getFirstgid() + m_tileset->getTileCount()) - 1; if (getGid() >= firstId && getGid() <= lastId) { int baseTilePosition = ((int)getGid() - firstId); int tileModX = (baseTilePosition % columns); int currentRow = (baseTilePosition / columns); int offsetX = (tileModX != 0) ? ((tileModX) * m_map->getTileSize().x) : (0 * m_map->getTileSize().x); int offsetY = (currentRow < rows-1) ? (currentRow * m_map->getTileSize().y) : ((rows-1) * m_map->getTileSize().y); m_drawingRect = { offsetX, offsetY, m_map->getTileSize().x, m_map->getTileSize().y }; } else m_drawingRect = {0, 0, 0, 0}; } /*! * Get the position of the tile in pixels based on the tile data position from the current layer. * @return The position of the tile in Pixels */ const tson::Vector2f tson::Tile::getPosition(const std::tuple &tileDataPos) { return {((float) std::get<0>(tileDataPos)) * m_drawingRect.width, ((float) std::get<1>(tileDataPos)) * m_drawingRect.height}; } // T i l e O b j e c t . h p p // --------------------- /*! * In cases where the empty constructor is called, this must be called manually * for this class to make sense * @param posInTileUnits * @param tile */ void tson::TileObject::initialize(const std::tuple &posInTileUnits, tson::Tile *tile) { m_tile = tile; m_posInTileUnits = tile->getPositionInTileUnits(posInTileUnits); m_position = tile->getPosition(posInTileUnits); } const tson::Rect &tson::TileObject::getDrawingRect() const { return m_tile->getDrawingRect(); } // L a y e r . h p p // ------------------- /*! * Decompresses data if there are matching decompressors */ void tson::Layer::decompressData() { tson::DecompressorContainer *container = m_map->getDecompressors(); if(container->empty()) return; if(m_encoding.empty() && m_compression.empty()) return; std::string data = m_base64Data; bool hasBeenDecoded = false; if(!m_encoding.empty() && container->contains(m_encoding)) { data = container->get(m_encoding)->decompress(data); hasBeenDecoded = true; } if(!m_compression.empty() && container->contains(m_compression)) { data = container->get(m_compression)->decompress(data); } if(hasBeenDecoded) { std::vector bytes = tson::Tools::Base64DecodedStringToBytes(data); m_data = tson::Tools::BytesToUnsignedInts(bytes); } } // W o r l d . h p p // ------------------ /*! * Loads the actual maps based on the world data. * @param parser A Tileson object used for parsing the maps of the world. * @return How many maps who were parsed. Remember to call getStatus() for the actual map to find out if everything went okay. */ int tson::World::loadMaps(tson::Tileson *parser) { m_maps.clear(); std::for_each(m_mapData.begin(), m_mapData.end(), [&](const tson::WorldMapData &data) { if(fs::exists(data.path)) { std::unique_ptr map = parser->parse(data.path); m_maps.push_back(std::move(map)); } }); return m_maps.size(); } #endif //TILESON_TILESON_FORWARD_HPP /*** End of inlined file: tileson_forward.hpp ***/ #endif //TILESON_TILESON_H