mirror of
https://github.com/quizhizhe/LiteLoaderBDS-1.16.40.git
synced 2025-06-06 12:03:39 +00:00
935 lines
27 KiB
C++
935 lines
27 KiB
C++
#ifndef NBT_HEAD_BNTYGTFREQXCPVMFFG
|
|
#define NBT_HEAD_BNTYGTFREQXCPVMFFG
|
|
|
|
#include <cinttypes>
|
|
#include <ostream>
|
|
#include <istream>
|
|
#include <memory>
|
|
#include <cstddef>
|
|
#include <type_traits>
|
|
#include <vector>
|
|
#include <map>
|
|
#include <cctype>
|
|
|
|
#if defined(__BYTE_ORDER) && __BYTE_ORDER == __BIG_ENDIAN || \
|
|
defined(__BIG_ENDIAN__) || \
|
|
defined(__ARMEB__) || \
|
|
defined(__THUMBEB__) || \
|
|
defined(__AARCH64EB__) || \
|
|
defined(_MIBSEB) || defined(__MIBSEB) || defined(__MIBSEB__)
|
|
# define NBT_BIG_ENDIAN
|
|
#elif defined(__BYTE_ORDER) && __BYTE_ORDER == __LITTLE_ENDIAN || \
|
|
defined(__LITTLE_ENDIAN__) || \
|
|
defined(__ARMEL__) || \
|
|
defined(__THUMBEL__) || \
|
|
defined(__AARCH64EL__) || \
|
|
defined(_MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) || \
|
|
defined(__MINGW32__)
|
|
#else
|
|
//# error "unknown architecture"
|
|
#endif
|
|
|
|
namespace nbt {
|
|
|
|
enum class tag_id : std::uint8_t {
|
|
tag_end, tag_byte, tag_short, tag_int, tag_long, tag_float,
|
|
tag_double, tag_bytearray, tag_string, tag_list, tag_compound,
|
|
tag_intarray, tag_longarray
|
|
};
|
|
|
|
} // namespace nbt
|
|
|
|
namespace std {
|
|
|
|
string to_string(nbt::tag_id tid);
|
|
|
|
} // namespace std
|
|
|
|
namespace nbt {
|
|
|
|
inline int cheof(std::istream & input) {
|
|
int value = input.get();
|
|
if (value == EOF)
|
|
throw std::runtime_error("EOF");
|
|
return value;
|
|
}
|
|
|
|
std::ostream & operator<<(std::ostream & output, tag_id tid);
|
|
|
|
template <typename number_t>
|
|
number_t reverse(number_t number) {
|
|
constexpr std::size_t n = sizeof(number_t);
|
|
union {
|
|
number_t number;
|
|
std::uint8_t data[n];
|
|
} tmp { number };
|
|
for (std::size_t i = 0; i < n/2; ++i) {
|
|
std::uint8_t z = tmp.data[i];
|
|
tmp.data[i] = tmp.data[n - 1 - i];
|
|
tmp.data[n - 1 - i] = z;
|
|
}
|
|
return tmp.number;
|
|
}
|
|
|
|
template <typename number_t>
|
|
number_t net_order(number_t number) {
|
|
#ifndef NBT_BIG_ENDIAN
|
|
return reverse(number);
|
|
#else
|
|
return number;
|
|
#endif
|
|
}
|
|
|
|
template <typename number_t>
|
|
number_t disk_order(number_t number) {
|
|
#ifdef NBT_BIG_ENDIAN
|
|
return reverse(number);
|
|
#else
|
|
return number;
|
|
#endif
|
|
}
|
|
|
|
struct context {
|
|
enum class orders {
|
|
big_endian, little_endian
|
|
} order;
|
|
enum class formats {
|
|
bin, zigzag, mojangson, zint
|
|
} format;
|
|
static const context & get(std::ios_base & ios);
|
|
void set(std::ios_base & ios) const;
|
|
};
|
|
|
|
inline std::istream & operator>>(std::istream & input, const context & ctxt) {
|
|
ctxt.set(input);
|
|
return input;
|
|
}
|
|
|
|
inline std::ostream & operator<<(std::ostream & output, const context & ctxt) {
|
|
ctxt.set(output);
|
|
return output;
|
|
}
|
|
|
|
namespace contexts {
|
|
|
|
inline const context java {
|
|
context::orders::big_endian,
|
|
context::formats::bin
|
|
}, bedrock_net {
|
|
context::orders::little_endian,
|
|
context::formats::zigzag
|
|
}, bedrock_disk {
|
|
context::orders::little_endian,
|
|
context::formats::bin
|
|
}, kbt {
|
|
context::orders::big_endian,
|
|
context::formats::zint
|
|
}, mojangson {
|
|
context::orders::big_endian,
|
|
context::formats::mojangson
|
|
};
|
|
|
|
} // namespace contexts
|
|
|
|
template <typename number_t>
|
|
number_t correct_order(number_t number, const context::orders order) {
|
|
switch (order) {
|
|
case context::orders::little_endian:
|
|
return disk_order(number);
|
|
case context::orders::big_endian:
|
|
default:
|
|
return net_order(number);
|
|
}
|
|
}
|
|
|
|
std::int32_t load_varint(std::istream & input);
|
|
|
|
void dump_varint(std::ostream & output, std::int32_t value);
|
|
|
|
std::int64_t load_varlong(std::istream & input);
|
|
|
|
void dump_varlong(std::ostream & output, std::int64_t value);
|
|
|
|
template <typename number_t>
|
|
number_t load_flat(std::istream & input, const context::orders order) {
|
|
constexpr std::size_t n = sizeof(number_t);
|
|
union {
|
|
number_t number;
|
|
char data[n];
|
|
} tmp;
|
|
input.read(tmp.data, n);
|
|
return correct_order(tmp.number, order);
|
|
}
|
|
|
|
template <typename number_t>
|
|
number_t load_text(std::istream & input) {
|
|
return static_cast<number_t>(load_text<std::make_signed_t<number_t>>(input));
|
|
}
|
|
|
|
template <>
|
|
std::int8_t load_text<std::int8_t>(std::istream & input);
|
|
|
|
template <>
|
|
std::int16_t load_text<std::int16_t>(std::istream & input);
|
|
|
|
template <>
|
|
std::int32_t load_text<std::int32_t>(std::istream & input);
|
|
|
|
template <>
|
|
std::int64_t load_text<std::int64_t>(std::istream & input);
|
|
|
|
template <>
|
|
float load_text<float>(std::istream & input);
|
|
|
|
template <>
|
|
double load_text<double>(std::istream & input);
|
|
|
|
template <typename number_t>
|
|
number_t load(std::istream & input, const context & ctxt) {
|
|
if (ctxt.format == context::formats::mojangson)
|
|
return load_text<number_t>(input);
|
|
else
|
|
return load_flat<number_t>(input, ctxt.order);
|
|
}
|
|
|
|
template <>
|
|
std::int32_t load<std::int32_t>(std::istream & input, const context & ctxt);
|
|
|
|
template <>
|
|
inline std::uint32_t load<std::uint32_t>(std::istream & input, const context & ctxt) {
|
|
return static_cast<std::uint32_t>(load<std::int32_t>(input, ctxt));
|
|
}
|
|
|
|
template <>
|
|
std::int64_t load<std::int64_t>(std::istream & input, const context & ctxt);
|
|
|
|
template <>
|
|
inline std::uint64_t load<std::uint64_t>(std::istream & input, const context & ctxt) {
|
|
return static_cast<std::uint64_t>(load<std::int64_t>(input, ctxt));
|
|
}
|
|
|
|
void skip_space(std::istream & input);
|
|
|
|
std::size_t load_size(std::istream & input, const context & ctxt);
|
|
void dump_size(std::ostream & output, const context & ctxt, std::size_t size);
|
|
|
|
template <typename number_t>
|
|
std::vector<number_t> load_array_text(std::istream & input) {
|
|
std::vector<number_t> result;
|
|
for (;;) {
|
|
skip_space(input);
|
|
char c = cheof(input);
|
|
input.putback(c);
|
|
if (c == ']') {
|
|
break;
|
|
}
|
|
result.push_back(load_text<number_t>(input));
|
|
skip_space(input);
|
|
char next = cheof(input);
|
|
if (next != ',') {
|
|
input.putback(next);
|
|
break;
|
|
}
|
|
}
|
|
result.shrink_to_fit();
|
|
return result;
|
|
}
|
|
|
|
template <typename number_t>
|
|
std::vector<number_t> load_array_bin(std::istream & input, const context & ctxt) {
|
|
auto size = load_size(input, ctxt);
|
|
std::vector<number_t> result;
|
|
result.reserve(size);
|
|
for (std::size_t i = 0; i < size; i++)
|
|
result.emplace_back(load<number_t>(input, ctxt));
|
|
return result;
|
|
}
|
|
|
|
template <typename number_t>
|
|
std::vector<number_t> load_array(std::istream & input, const context & ctxt) {
|
|
if (ctxt.format == context::formats::mojangson)
|
|
return load_array_text<number_t>(input);
|
|
else
|
|
return load_array_bin<number_t>(input, ctxt);
|
|
}
|
|
|
|
template <typename number_t>
|
|
void dump_flat(std::ostream & output, number_t number, const context::orders order) {
|
|
constexpr std::size_t n = sizeof(number_t);
|
|
union {
|
|
number_t number;
|
|
char data[n];
|
|
} tmp { correct_order(number, order) };
|
|
output.write(tmp.data, n);
|
|
}
|
|
|
|
template <typename number_t>
|
|
void dump_text(std::ostream & output, number_t number) {
|
|
dump_text(output, std::make_signed_t<number_t>(number));
|
|
}
|
|
|
|
template <>
|
|
void dump_text<std::int8_t>(std::ostream & output, std::int8_t number);
|
|
|
|
template <>
|
|
void dump_text<std::int16_t>(std::ostream & output, std::int16_t number);
|
|
|
|
template <>
|
|
void dump_text<std::int32_t>(std::ostream & output, std::int32_t number);
|
|
|
|
template <>
|
|
void dump_text<std::int64_t>(std::ostream & output, std::int64_t number);
|
|
|
|
template <>
|
|
void dump_text<float>(std::ostream & output, float number);
|
|
|
|
template <>
|
|
void dump_text<double>(std::ostream & output, double number);
|
|
|
|
template <typename number_t>
|
|
void dump(std::ostream & output, number_t number, const context & ctxt) {
|
|
if (ctxt.format == context::formats::mojangson)
|
|
dump_text(output, number);
|
|
else
|
|
dump_flat(output, number, ctxt.order);
|
|
}
|
|
|
|
template <>
|
|
void dump<std::int32_t>(std::ostream & output, std::int32_t number, const context & ctxt);
|
|
|
|
template <>
|
|
inline void dump<std::uint32_t>(std::ostream & output, std::uint32_t number, const context & ctxt) {
|
|
dump(output, static_cast<std::int32_t>(number), ctxt);
|
|
}
|
|
|
|
template <>
|
|
void dump<std::int64_t>(std::ostream & output, std::int64_t number, const context & ctxt);
|
|
|
|
template <>
|
|
inline void dump<std::uint64_t>(std::ostream & output, std::uint64_t number, const context & ctxt) {
|
|
dump(output, static_cast<std::int64_t>(number), ctxt);
|
|
}
|
|
|
|
template <typename number_t>
|
|
void dump_array_text(std::ostream & output, const std::vector<number_t> & array) {
|
|
auto iter = array.cbegin();
|
|
auto end = array.cend();
|
|
if (iter == end)
|
|
return;
|
|
dump_text(output, *iter);
|
|
for (++iter; iter != end; ++iter) {
|
|
output << ',';
|
|
dump_text(output, *iter);
|
|
}
|
|
}
|
|
|
|
template <typename number_t>
|
|
void dump_array_bin(std::ostream & output, const std::vector<number_t> & array, const context & ctxt) {
|
|
dump_size(output, ctxt, array.size());
|
|
for (const auto & element : array)
|
|
dump(output, element, ctxt);
|
|
}
|
|
|
|
template <typename number_t>
|
|
void dump_array(std::ostream & output, const std::vector<number_t> & array, const context & ctxt) {
|
|
if (ctxt.format == context::formats::mojangson)
|
|
dump_array_text(output, array);
|
|
else
|
|
dump_array_bin(output, array, ctxt);
|
|
}
|
|
|
|
template <typename element_type, typename F>
|
|
void dump_list(std::ostream & output, tag_id aid, const std::vector<element_type> & list, F action) {
|
|
const context & ctxt = context::get(output);
|
|
if (ctxt.format == context::formats::mojangson) {
|
|
output << '[';
|
|
} else {
|
|
output.put(static_cast<char>(aid));
|
|
auto size = static_cast<std::size_t>(list.size());
|
|
dump_size(output, ctxt, size);
|
|
}
|
|
auto iter = list.cbegin();
|
|
auto end = list.cend();
|
|
if (iter != end) {
|
|
action(*iter, ctxt);
|
|
for (++iter; iter != end; ++iter) {
|
|
if (ctxt.format == context::formats::mojangson)
|
|
output << ',';
|
|
action(*iter, ctxt);
|
|
}
|
|
}
|
|
if (ctxt.format == context::formats::mojangson)
|
|
output << ']';
|
|
}
|
|
|
|
template <typename tag_type, typename F>
|
|
std::unique_ptr<tag_type> load_list(std::istream & input, F action) {
|
|
const context & ctxt = context::get(input);
|
|
auto ptr = std::make_unique<tag_type>();
|
|
typename tag_type::value_type & result = ptr->value;
|
|
if (ctxt.format != context::formats::mojangson) {
|
|
std::size_t size = load_size(input, ctxt);
|
|
result.reserve(size);
|
|
for (std::size_t i = 0; i < size; i++)
|
|
result.emplace_back(action(ctxt));
|
|
} else {
|
|
for (;;) {
|
|
result.emplace_back(action(ctxt));
|
|
skip_space(input);
|
|
int next = cheof(input);
|
|
if (next != ',') {
|
|
input.putback(next);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
return ptr;
|
|
}
|
|
|
|
tag_id deduce_tag(std::istream & input);
|
|
|
|
namespace tags {
|
|
|
|
struct tag {
|
|
virtual tag_id id() const noexcept = 0;
|
|
virtual void write(std::ostream & output) const = 0;
|
|
virtual std::unique_ptr<tag> copy() const = 0;
|
|
virtual ~tag() {}
|
|
};
|
|
|
|
template <tag_id tid>
|
|
struct find_by;
|
|
|
|
template <typename value_type>
|
|
struct find_of;
|
|
|
|
std::unique_ptr<tag> read(tag_id tid, std::istream & input);
|
|
|
|
template <typename tag_type>
|
|
std::unique_ptr<tag_type> cast(std::unique_ptr<tag> && ptr) {
|
|
static_assert(std::is_base_of<tag, tag_type>::value);
|
|
std::unique_ptr<tag_type> result(dynamic_cast<tag_type *>(ptr.release()));
|
|
return result;
|
|
}
|
|
|
|
template <typename tag_type>
|
|
std::unique_ptr<const tag_type> cast(std::unique_ptr<const tag> & ptr) {
|
|
static_assert(std::is_base_of<tag, tag_type>::value);
|
|
std::unique_ptr<const tag_type> result(dynamic_cast<const tag_type *>(ptr.release()));
|
|
return result;
|
|
}
|
|
|
|
# define TAG_FIND(T) \
|
|
template <> \
|
|
struct find_by<T::tid> final { \
|
|
typedef T type; \
|
|
}; \
|
|
template <> \
|
|
struct find_of<T::value_type> final { \
|
|
typedef T type; \
|
|
};
|
|
|
|
template <tag_id tid>
|
|
using tag_by = typename find_by<tid>::type;
|
|
|
|
template <typename value_type>
|
|
using tag_of = typename find_of<value_type>::type;
|
|
|
|
struct end_tag final : public tag {
|
|
typedef std::nullptr_t value_type;
|
|
static constexpr tag_id tid = tag_id::tag_end;
|
|
virtual tag_id id() const noexcept override {
|
|
return tid;
|
|
}
|
|
virtual void write(std::ostream & output) const override;
|
|
virtual std::unique_ptr<tag> copy() const override;
|
|
};
|
|
inline end_tag end;
|
|
TAG_FIND(end_tag)
|
|
|
|
template <tag_id TID, typename number_t>
|
|
struct numeric_tag : public tag {
|
|
typedef number_t value_type;
|
|
value_type value;
|
|
static constexpr tag_id tid = TID;
|
|
virtual tag_id id() const noexcept override {
|
|
return tid;
|
|
}
|
|
numeric_tag() = default;
|
|
constexpr numeric_tag(value_type number) : value(number) {}
|
|
static std::unique_ptr<numeric_tag> read(std::istream & input) {
|
|
return std::make_unique<numeric_tag>(load<value_type>(input, context::get(input)));
|
|
}
|
|
virtual void write(std::ostream & output) const override {
|
|
dump(output, value, context::get(output));
|
|
}
|
|
virtual std::unique_ptr<tag> copy() const override {
|
|
return std::make_unique<numeric_tag>(value);
|
|
}
|
|
};
|
|
|
|
# define NUMERIC_TAG(name, tid, number_t) \
|
|
using name = numeric_tag<tid, number_t>; \
|
|
TAG_FIND(name)
|
|
|
|
NUMERIC_TAG(byte_tag, tag_id::tag_byte, std::int8_t)
|
|
NUMERIC_TAG(short_tag, tag_id::tag_short, std::int16_t)
|
|
NUMERIC_TAG(int_tag, tag_id::tag_int, std::int32_t)
|
|
NUMERIC_TAG(long_tag, tag_id::tag_long, std::int64_t)
|
|
NUMERIC_TAG(float_tag, tag_id::tag_float, float)
|
|
NUMERIC_TAG(double_tag, tag_id::tag_double, double)
|
|
|
|
# undef NUMERIC_TAG
|
|
|
|
template <tag_id TID, typename number_t, char prefix>
|
|
struct array_tag final : public tag {
|
|
typedef number_t element_type;
|
|
typedef std::vector<element_type> value_type;
|
|
value_type value;
|
|
static constexpr tag_id tid = TID;
|
|
virtual tag_id id() const noexcept override {
|
|
return tid;
|
|
}
|
|
array_tag() = default;
|
|
array_tag(const value_type & array) : value(array) {}
|
|
array_tag(value_type && array) : value(std::move(array)) {}
|
|
static std::unique_ptr<array_tag> read(std::istream & input) {
|
|
const context & ctxt = context::get(input);
|
|
if (ctxt.format == context::formats::mojangson) {
|
|
skip_space(input);
|
|
char a = cheof(input);
|
|
if (a != '[')
|
|
throw std::runtime_error("failed to open array tag");
|
|
a = cheof(input);
|
|
if (a != prefix)
|
|
throw std::runtime_error("wrong array tag type");
|
|
a = cheof(input);
|
|
if (a != ';')
|
|
throw std::runtime_error("unexpected symbol in array tag");
|
|
}
|
|
auto result = std::make_unique<array_tag>(load_array<element_type>(input, ctxt));
|
|
if (ctxt.format == context::formats::mojangson) {
|
|
char a = cheof(input);
|
|
if (a != ']')
|
|
throw std::runtime_error("failed to close array tag");
|
|
}
|
|
return result;
|
|
}
|
|
virtual void write(std::ostream & output) const override {
|
|
const context & ctxt = context::get(output);
|
|
if (ctxt.format == context::formats::mojangson)
|
|
output << '[' << prefix << ';';
|
|
dump_array(output, value, ctxt);
|
|
if (ctxt.format == context::formats::mojangson)
|
|
output << ']';
|
|
}
|
|
virtual std::unique_ptr<tag> copy() const override {
|
|
return std::make_unique<array_tag>(value);
|
|
}
|
|
};
|
|
|
|
# define ARRAY_TAG(name, tid, number_t, prefix) \
|
|
using name = array_tag<tid, number_t, prefix>; \
|
|
TAG_FIND(name)
|
|
|
|
ARRAY_TAG(bytearray_tag, tag_id::tag_bytearray, std::int8_t, 'B')
|
|
ARRAY_TAG(intarray_tag, tag_id::tag_intarray, std::int32_t, 'I')
|
|
ARRAY_TAG(longarray_tag, tag_id::tag_longarray, std::int64_t, 'L')
|
|
|
|
# undef ARRAY_TAG
|
|
|
|
struct string_tag final : public tag {
|
|
typedef std::string value_type;
|
|
value_type value;
|
|
static constexpr tag_id tid = tag_id::tag_string;
|
|
virtual tag_id id() const noexcept override {
|
|
return tag_id::tag_string;
|
|
}
|
|
string_tag() = default;
|
|
string_tag(const value_type & string);
|
|
string_tag(value_type && string);
|
|
static std::unique_ptr<string_tag> read(std::istream & input);
|
|
virtual void write(std::ostream & output) const override;
|
|
virtual std::unique_ptr<tag> copy() const override;
|
|
};
|
|
|
|
TAG_FIND(string_tag)
|
|
|
|
struct tag_list_tag;
|
|
|
|
struct list_tag : public tag {
|
|
static constexpr tag_id tid = tag_id::tag_list;
|
|
virtual tag_id id() const noexcept override {
|
|
return tag_id::tag_list;
|
|
}
|
|
virtual tag_id element_id() const noexcept = 0;
|
|
virtual size_t size() const noexcept = 0;
|
|
virtual bool heavy() const noexcept = 0;
|
|
virtual std::unique_ptr<tag> operator[](size_t i) const = 0;
|
|
static std::unique_ptr<list_tag> read(std::istream & input);
|
|
virtual tag_list_tag as_tags() = 0;
|
|
};
|
|
|
|
template <>
|
|
struct find_by<tag_id::tag_list> final {
|
|
typedef list_tag type;
|
|
};
|
|
|
|
struct tag_list_tag final : public list_tag {
|
|
tag_id eid;
|
|
typedef std::unique_ptr<tag> element_type;
|
|
typedef std::vector<element_type> value_type;
|
|
value_type value;
|
|
virtual tag_id element_id() const noexcept override {
|
|
return value.empty() ? eid : (eid != tag_id::tag_end ? eid : value[0]->id());
|
|
}
|
|
virtual size_t size() const noexcept override {
|
|
return value.size();
|
|
}
|
|
virtual bool heavy() const noexcept override {
|
|
return true;
|
|
}
|
|
virtual std::unique_ptr<tag> operator[](size_t i) const override;
|
|
tag_list_tag();
|
|
explicit tag_list_tag(tag_id tid);
|
|
tag_list_tag(const tag_list_tag & other);
|
|
tag_list_tag(const value_type & list, tag_id tid = tag_id::tag_end);
|
|
tag_list_tag(value_type && list, tag_id tid = tag_id::tag_end);
|
|
virtual void write(std::ostream & output) const override;
|
|
virtual std::unique_ptr<tag> copy() const override;
|
|
virtual tag_list_tag as_tags() override;
|
|
};
|
|
|
|
template <tag_id tid>
|
|
struct find_list_by;
|
|
|
|
template <typename value_type>
|
|
struct find_list_of;
|
|
|
|
template <tag_id tid>
|
|
using list_by = typename find_list_by<tid>::type;
|
|
|
|
template <typename value_type>
|
|
using list_of = typename find_list_of<value_type>::type;
|
|
|
|
# define FIND_LIST_TAG(name) \
|
|
template <> \
|
|
struct find_list_by<name::eid> { \
|
|
typedef name type; \
|
|
}; \
|
|
template <> \
|
|
struct find_list_of<name::element_type> { \
|
|
typedef name type; \
|
|
};
|
|
|
|
struct end_list_tag final : public list_tag {
|
|
typedef end_tag tag_type;
|
|
static_assert(std::is_base_of<tag, tag_type>::value);
|
|
typedef typename end_tag::value_type element_type;
|
|
typedef std::vector<element_type> value_type;
|
|
static constexpr tag_id eid = tag_type::tid;
|
|
virtual tag_id element_id() const noexcept override {
|
|
return eid;
|
|
}
|
|
virtual size_t size() const noexcept override {
|
|
return 0u;
|
|
}
|
|
virtual bool heavy() const noexcept override {
|
|
return false;
|
|
}
|
|
virtual std::unique_ptr<tag> operator[](size_t i) const override;
|
|
end_list_tag() = default;
|
|
static std::unique_ptr<end_list_tag> read_content(std::istream & input);
|
|
virtual void write(std::ostream & output) const override;
|
|
virtual std::unique_ptr<tag> copy() const override;
|
|
virtual tag_list_tag as_tags() override;
|
|
};
|
|
inline end_list_tag end_list;
|
|
|
|
FIND_LIST_TAG(end_list_tag)
|
|
|
|
template <typename T>
|
|
struct number_list_tag final : public list_tag {
|
|
typedef T tag_type;
|
|
static_assert(std::is_base_of<tag, tag_type>::value);
|
|
typedef typename T::value_type element_type;
|
|
typedef std::vector<element_type> value_type;
|
|
static constexpr tag_id eid = tag_type::tid;
|
|
virtual tag_id element_id() const noexcept override {
|
|
return eid;
|
|
}
|
|
value_type value;
|
|
virtual size_t size() const noexcept override {
|
|
return value.size();
|
|
}
|
|
virtual bool heavy() const noexcept override {
|
|
return false;
|
|
}
|
|
virtual std::unique_ptr<tag> operator[](size_t i) const override {
|
|
return std::make_unique<tag_type>(value.at(i));
|
|
}
|
|
number_list_tag() = default;
|
|
number_list_tag(const value_type & list) : value(list) {}
|
|
number_list_tag(value_type && list) : value(std::move(list)) {}
|
|
static std::unique_ptr<number_list_tag> read_content(std::istream & input) {
|
|
return load_list<number_list_tag>(input, [&input](const context & ctxt) -> element_type {
|
|
return load<element_type>(input, ctxt);
|
|
});
|
|
}
|
|
virtual void write(std::ostream & output) const override {
|
|
dump_list(output, eid, value, [&output](const element_type & number, const context & ctxt) {
|
|
dump(output, number, ctxt);
|
|
});
|
|
}
|
|
virtual std::unique_ptr<tag> copy() const override {
|
|
return std::make_unique<number_list_tag>(*this);
|
|
}
|
|
virtual tag_list_tag as_tags() override {
|
|
tag_list_tag result(eid);
|
|
result.value.reserve(value.size());
|
|
for (auto each : value)
|
|
result.value.push_back(std::make_unique<tag_type>(each));
|
|
value.clear();
|
|
value.shrink_to_fit();
|
|
return result;
|
|
}
|
|
};
|
|
|
|
# define NUMBER_LIST_TAG(name, tag_type) \
|
|
using name = number_list_tag<tag_type>; \
|
|
FIND_LIST_TAG(name)
|
|
|
|
NUMBER_LIST_TAG(byte_list_tag, byte_tag)
|
|
NUMBER_LIST_TAG(short_list_tag, short_tag)
|
|
NUMBER_LIST_TAG(int_list_tag, int_tag)
|
|
NUMBER_LIST_TAG(long_list_tag, long_tag)
|
|
NUMBER_LIST_TAG(float_list_tag, float_tag)
|
|
NUMBER_LIST_TAG(double_list_tag, double_tag)
|
|
|
|
# undef NUMBER_LIST_TAG
|
|
|
|
template <typename T>
|
|
struct array_list_tag final : public list_tag {
|
|
typedef T tag_type;
|
|
static_assert(std::is_base_of<tag, tag_type>::value);
|
|
typedef typename T::value_type element_type;
|
|
typedef std::vector<element_type> value_type;
|
|
static constexpr tag_id eid = tag_type::tid;
|
|
virtual tag_id element_id() const noexcept override {
|
|
return eid;
|
|
}
|
|
value_type value;
|
|
virtual size_t size() const noexcept override {
|
|
return value.size();
|
|
}
|
|
virtual bool heavy() const noexcept override {
|
|
return false;
|
|
}
|
|
virtual std::unique_ptr<tag> operator[](size_t i) const override {
|
|
return std::make_unique<tag_type>(value.at(i));
|
|
}
|
|
array_list_tag() = default;
|
|
array_list_tag(const value_type & list) : value(list) {}
|
|
array_list_tag(value_type && list) : value(std::move(list)) {}
|
|
static std::unique_ptr<array_list_tag> read_content(std::istream & input) {
|
|
return load_list<array_list_tag>(input, [&input] (const context & ctxt) -> element_type {
|
|
return load_array<typename element_type::value_type>(input, ctxt);
|
|
});
|
|
}
|
|
virtual void write(std::ostream & output) const override {
|
|
dump_list(output, eid, value, [&output]
|
|
(const element_type & element, const context & ctxt) {
|
|
dump_array(output, element, ctxt);
|
|
});
|
|
}
|
|
virtual std::unique_ptr<tag> copy() const override {
|
|
return std::make_unique<array_list_tag>(*this);
|
|
}
|
|
virtual tag_list_tag as_tags() override {
|
|
tag_list_tag result(eid);
|
|
result.value.reserve(value.size());
|
|
for (auto & each : value)
|
|
result.value.push_back(std::make_unique<tag_type>(std::move(each)));
|
|
value.clear();
|
|
value.shrink_to_fit();
|
|
return result;
|
|
}
|
|
};
|
|
# define ARRAY_LIST_TAG(name, tag_type) \
|
|
using name = array_list_tag<tag_type>; \
|
|
FIND_LIST_TAG(name)
|
|
|
|
ARRAY_LIST_TAG(bytearray_list_tag, bytearray_tag)
|
|
ARRAY_LIST_TAG(intarray_list_tag, intarray_tag)
|
|
ARRAY_LIST_TAG(longarray_list_tag, longarray_tag)
|
|
|
|
# undef ARRAY_LIST_TAG
|
|
|
|
struct string_list_tag final : public list_tag {
|
|
typedef string_tag tag_type;
|
|
typedef std::string element_type;
|
|
typedef std::vector<element_type> value_type;
|
|
static constexpr tag_id eid = tag_type::tid;
|
|
virtual tag_id element_id() const noexcept override {
|
|
return eid;
|
|
}
|
|
value_type value;
|
|
virtual size_t size() const noexcept override {
|
|
return value.size();
|
|
}
|
|
virtual bool heavy() const noexcept override {
|
|
return false;
|
|
}
|
|
virtual std::unique_ptr<tag> operator[](size_t i) const override;
|
|
string_list_tag() = default;
|
|
string_list_tag(const value_type & list);
|
|
string_list_tag(value_type && list);
|
|
static std::unique_ptr<string_list_tag> read_content(std::istream & input);
|
|
virtual void write(std::ostream & output) const override;
|
|
virtual std::unique_ptr<tag> copy() const override;
|
|
virtual tag_list_tag as_tags() override;
|
|
};
|
|
|
|
FIND_LIST_TAG(string_list_tag)
|
|
|
|
struct list_list_tag final : public list_tag {
|
|
typedef list_tag tag_type;
|
|
typedef std::unique_ptr<tag_type> element_type;
|
|
typedef std::vector<element_type> value_type;
|
|
static constexpr tag_id eid = tag_type::tid;
|
|
virtual tag_id element_id() const noexcept override {
|
|
return eid;
|
|
}
|
|
value_type value;
|
|
virtual size_t size() const noexcept override {
|
|
return value.size();
|
|
}
|
|
virtual bool heavy() const noexcept override {
|
|
return false;
|
|
}
|
|
virtual std::unique_ptr<tag> operator[](size_t i) const override;
|
|
list_list_tag() = default;
|
|
list_list_tag(const list_list_tag & other);
|
|
list_list_tag(const value_type & list);
|
|
list_list_tag(value_type && list);
|
|
static std::unique_ptr<list_list_tag> read_content(std::istream & input);
|
|
virtual void write(std::ostream & output) const override;
|
|
virtual std::unique_ptr<tag> copy() const override;
|
|
virtual tag_list_tag as_tags() override;
|
|
};
|
|
|
|
FIND_LIST_TAG(list_list_tag)
|
|
|
|
struct compound_tag final : public tag {
|
|
typedef std::map<std::string, std::unique_ptr<tag>> value_type;
|
|
bool is_root = false;
|
|
value_type value;
|
|
static constexpr tag_id tid = tag_id::tag_compound;
|
|
virtual tag_id id() const noexcept override {
|
|
return tag_id::tag_compound;
|
|
}
|
|
compound_tag() = default;
|
|
compound_tag(const compound_tag & other);
|
|
explicit compound_tag(bool root);
|
|
compound_tag(const value_type & map, bool root = false);
|
|
compound_tag(value_type && map, bool root = false);
|
|
static std::unique_ptr<compound_tag> read(std::istream & input);
|
|
virtual void write(std::ostream & output) const override;
|
|
virtual std::unique_ptr<tag> copy() const override;
|
|
compound_tag & operator=(const compound_tag & other);
|
|
void make_heavy();
|
|
|
|
template <typename T>
|
|
auto put(std::string && name, T && item) {
|
|
return value.insert_or_assign(std::move(name), std::make_unique<tag_of<T>>(std::move(item)));
|
|
}
|
|
|
|
template <typename T>
|
|
auto put(const std::string & name, T && item) {
|
|
return value.insert_or_assign(name, std::make_unique<tag_of<T>>(item));
|
|
}
|
|
|
|
template <typename T>
|
|
typename tag_of<T>::value_type & get(const std::string & name) {
|
|
return dynamic_cast<tag_of<T> &>(*value.at(name)).value;
|
|
}
|
|
|
|
template <typename T>
|
|
const typename tag_of<T>::value_type & get(const std::string & name) const {
|
|
return dynamic_cast<const tag_of<T> &>(*value.at(name)).value;
|
|
}
|
|
|
|
template <typename T>
|
|
typename tag_of<T>::value_type & tag(const std::string & name) {
|
|
auto iter = value.find(name);
|
|
if (iter == value.end()) {
|
|
auto ptr = std::make_unique<tag_of<T>>();
|
|
typename tag_of<T>::value_type & result = ptr->value;
|
|
value.emplace(name, std::move(ptr));
|
|
return result;
|
|
} else {
|
|
return dynamic_cast<tag_of<T> &>(*iter->second).value;
|
|
}
|
|
}
|
|
|
|
bool erase(const std::string & name);
|
|
};
|
|
|
|
TAG_FIND(compound_tag)
|
|
|
|
struct compound_list_tag final : public list_tag {
|
|
typedef compound_tag tag_type;
|
|
typedef tag_type element_type;
|
|
typedef std::vector<element_type> value_type;
|
|
static constexpr tag_id eid = tag_type::tid;
|
|
virtual tag_id element_id() const noexcept override {
|
|
return eid;
|
|
}
|
|
value_type value;
|
|
virtual size_t size() const noexcept override {
|
|
return value.size();
|
|
}
|
|
virtual bool heavy() const noexcept override {
|
|
return false;
|
|
}
|
|
virtual std::unique_ptr<tag> operator[](size_t i) const override;
|
|
compound_list_tag() = default;
|
|
compound_list_tag(const value_type & list);
|
|
compound_list_tag(value_type && list);
|
|
static std::unique_ptr<compound_list_tag> read_content(std::istream & input);
|
|
virtual void write(std::ostream & output) const override;
|
|
virtual std::unique_ptr<tag> copy() const override;
|
|
virtual tag_list_tag as_tags() override;
|
|
};
|
|
|
|
FIND_LIST_TAG(compound_list_tag)
|
|
|
|
# undef FIND_LIST_TAG
|
|
# undef TAG_FIND
|
|
|
|
|
|
template <typename T>
|
|
tag_of<T> wrap(T && value) {
|
|
return tag_of<T>(value);
|
|
}
|
|
|
|
template <tag_id tid>
|
|
std::unique_ptr<tag> read(std::istream & input) {
|
|
return tag_by<tid>::read(input);
|
|
}
|
|
|
|
} // namespace tags
|
|
|
|
std::istream & operator>>(std::istream & input, tags::compound_tag & compound);
|
|
std::ostream & operator<<(std::ostream & output, const tags::tag & tag);
|
|
|
|
} // namespace nbt
|
|
|
|
namespace std {
|
|
|
|
string to_string(const nbt::tags::tag & tag);
|
|
|
|
} // namespace std
|
|
|
|
#endif // NBT_HEAD_BNTYGTFREQXCPVMFFG
|