Skip to content

Instantly share code, notes, and snippets.

@ErisianArchitect
Created August 17, 2021 04:46
Show Gist options
  • Save ErisianArchitect/3c5b4d5459eccba9858a4995b23c27f1 to your computer and use it in GitHub Desktop.
Save ErisianArchitect/3c5b4d5459eccba9858a4995b23c27f1 to your computer and use it in GitHub Desktop.
#ifndef NBT_TAGS_HEADER_FILE
#define NBT_TAGS_HEADER_FILE
// https://minecraft.fandom.com/wiki/NBT_format
#include <string>
#include <string_view>
#include <vector>
#include <variant>
#include <cstdint>
#include <cstddef>
#include <stdexcept>
#include <algorithm>
namespace nbt
{
enum class tag : unsigned char
{
NONE = 0,
BYTE = 1,
SHORT = 2,
INT = 3,
LONG = 4,
FLOAT = 5,
DOUBLE = 6,
BYTEARRAY = 7,
STRING = 8,
LIST = 9,
COMPOUND = 10,
INTARRAY = 11,
LONGARRAY = 12,
};
using t_byte = int8_t;
using t_short = int16_t;
using t_int = int32_t;
using t_long = int64_t;
using t_float = float;
using t_double = double;
using t_string = std::string;
using t_bytearray = std::vector<std::byte>;
using t_intarray = std::vector<int32_t>;
using t_longarray = std::vector<int64_t>;
class t_list;
class t_compound;
template<typename T>
struct is_nbt_t
{
constexpr static bool value =
std::is_same<t_byte, T>::value || // 1
std::is_same<t_short, T>::value || // 2
std::is_same<t_int, T>::value || // 3
std::is_same<t_long, T>::value || // 4
std::is_same<t_float, T>::value || // 5
std::is_same<t_double, T>::value || // 6
std::is_same<t_bytearray, T>::value || // 7
std::is_same<t_string, T>::value || // 8
std::is_same<t_list, T>::value || // 9
std::is_same<t_compound, T>::value || // 10
std::is_same<t_intarray, T>::value || // 11
std::is_same<t_longarray, T>::value; // 12
};
template<typename T>
constexpr tag TagType()
{
static_assert(is_nbt_t<T>::value, "Must be NBT tag type.");
if constexpr (std::is_same<t_byte, T>::value)
return tag::BYTE;
if constexpr (std::is_same<t_short, T>::value)
return tag::SHORT;
if constexpr (std::is_same<t_int, T>::value)
return tag::INT;
if constexpr (std::is_same<t_long, T>::value)
return tag::LONG;
if constexpr (std::is_same<t_float, T>::value)
return tag::FLOAT;
if constexpr (std::is_same<t_double, T>::value)
return tag::DOUBLE;
if constexpr (std::is_same<t_bytearray, T>::value)
return tag::BYTEARRAY;
if constexpr (std::is_same<t_string, T>::value)
return tag::STRING;
if constexpr (std::is_same<t_list, T>::value)
return tag::LIST;
if constexpr (std::is_same<t_compound, T>::value)
return tag::COMPOUND;
if constexpr (std::is_same<t_intarray, T>::value)
return tag::INTARRAY;
if constexpr (std::is_same<t_longarray, T>::value)
return tag::LONGARRAY;
return tag::NONE;
}
class t_list
{
public:
// t_list uses std::variant that combines vectors of all tag types, as well as nullptr_t (for setting to null)
// This allows us to work directly with tag data rather than "boxed" tag data.
// Notice the commented index on the right hand side.
// This is also the tag ID for each type.
// This means that we can get the tag type like so:
// tag id = tag(var.index());
// where var is of type list_variant.
using list_variant = std::variant<
std::nullptr_t, // 0
std::vector<t_byte>, // 1
std::vector<t_short>, // 2
std::vector<t_int>, // 3
std::vector<t_long>, // 4
std::vector<t_float>, // 5
std::vector<t_double>, // 6
std::vector<t_bytearray>, // 7
std::vector<t_string>, // 8
std::vector<t_list>, // 9
std::vector<t_compound>, // 10
std::vector<t_intarray>, // 11
std::vector<t_longarray> // 12
>; // list_variant
list_variant data;
t_list() = default;
t_list(tag type)
{
switch (type)
{
case tag::BYTE:
data = std::vector<t_byte>();
break;
case tag::SHORT:
data = std::vector<t_short>();
break;
case tag::INT:
data = std::vector<t_int>();
break;
case tag::LONG :
data = std::vector<t_long>();
break;
case tag::FLOAT:
data = std::vector<t_float>();
break;
case tag::DOUBLE:
data = std::vector<t_double>();
break;
case tag::BYTEARRAY:
data = std::vector<t_bytearray>();
break;
case tag::STRING:
data = std::vector<t_string>();
break;
case tag::LIST:
data = std::vector<t_list>();
break;
case tag::COMPOUND:
data = std::vector<t_compound>();
break;
case tag::INTARRAY:
data = std::vector<t_intarray>();
break;
case tag::LONGARRAY:
data = std::vector<t_longarray>();
break;
default:
data = nullptr;
break;
}
}
t_list(const t_list& rhs) = default;
t_list(t_list&& rhs) = default;
t_list(const std::vector<t_byte>& cdata) : data(cdata) {}
t_list(std::vector<t_byte>&& rdata) : data(rdata) {}
t_list(const std::vector<t_short>& cdata) : data(cdata) {}
t_list(std::vector<t_short>&& rdata) : data(rdata) {}
t_list(const std::vector<t_int>& cdata) : data(cdata) {}
t_list(std::vector<t_int>&& rdata) : data(rdata) {}
t_list(const std::vector<t_long>& cdata) : data(cdata) {}
t_list(std::vector<t_long>&& rdata) : data(rdata) {}
t_list(const std::vector<t_float>& cdata) : data(cdata) {}
t_list(std::vector<t_float>&& rdata) : data(rdata) {}
t_list(const std::vector<t_double>& cdata) : data(cdata) {}
t_list(std::vector<t_double>&& rdata) : data(rdata) {}
t_list(const std::vector<t_bytearray>& cdata) : data(cdata) {}
t_list(std::vector<t_bytearray>&& rdata) : data(rdata) {}
t_list(const std::vector<t_string>& cdata) : data(cdata) {}
t_list(std::vector<t_string>&& rdata) : data(rdata) {}
t_list(const std::vector<t_list>& cdata) : data(cdata) {}
t_list(std::vector<t_list>&& rdata) : data(rdata) {}
t_list(const std::vector<t_compound>& cdata) : data(cdata) {}
t_list(std::vector<t_compound>&& rdata) : data(rdata) {}
t_list(const std::vector<t_intarray>& cdata) : data(cdata) {}
t_list(std::vector<t_intarray>&& rdata) : data(rdata) {}
t_list(const std::vector<t_longarray>& cdata) : data(cdata) {}
t_list(std::vector<t_longarray>&& rdata) : data(rdata) {}
t_list& operator=(const t_list& rhs) = default;
t_list& operator=(t_list&& rhs) = default;
t_list& operator=(const std::vector<t_byte>& cdata) { this->data = cdata; return *this; }
t_list& operator=(std::vector<t_byte>&& rdata) { this->data = rdata; return *this; }
t_list& operator=(const std::vector<t_short>& cdata) { this->data = cdata; return *this; }
t_list& operator=(std::vector<t_short>&& rdata) { this->data = rdata; return *this; }
t_list& operator=(const std::vector<t_int>& cdata) { this->data = cdata; return *this; }
t_list& operator=(std::vector<t_int>&& rdata) { this->data = rdata; return *this; }
t_list& operator=(const std::vector<t_long>& cdata) { this->data = cdata; return *this; }
t_list& operator=(std::vector<t_long>&& rdata) { this->data = rdata; return *this; }
t_list& operator=(const std::vector<t_float>& cdata) { this->data = cdata; return *this; }
t_list& operator=(std::vector<t_float>&& rdata) { this->data = rdata; return *this; }
t_list& operator=(const std::vector<t_double>& cdata) { this->data = cdata; return *this; }
t_list& operator=(std::vector<t_double>&& rdata) { this->data = rdata; return *this; }
t_list& operator=(const std::vector<t_bytearray>& cdata) { this->data = cdata; return *this; }
t_list& operator=(std::vector<t_bytearray>&& rdata) { this->data = rdata; return *this; }
t_list& operator=(const std::vector<t_string>& cdata) { this->data = cdata; return *this; }
t_list& operator=(std::vector<t_string>&& rdata) { this->data = rdata; return *this; }
t_list& operator=(const std::vector<t_list>& cdata) { this->data = cdata; return *this; }
t_list& operator=(std::vector<t_list>&& rdata) { this->data = rdata; return *this; }
t_list& operator=(const std::vector<t_compound>& cdata) { this->data = cdata; return *this; }
t_list& operator=(std::vector<t_compound>&& rdata) { this->data = rdata; return *this; }
t_list& operator=(const std::vector<t_intarray>& cdata) { this->data = cdata; return *this; }
t_list& operator=(std::vector<t_intarray>&& rdata) { this->data = rdata; return *this; }
t_list& operator=(const std::vector<t_longarray>& cdata) { this->data = cdata; return *this; }
t_list& operator=(std::vector<t_longarray>&& rdata) { this->data = rdata; return *this; }
template<typename T>
[[nodiscard]] std::vector<T>* as_vector()
{
static_assert(is_nbt_t<T>::value, "Must be NBT type.");
if (this->type() == TagType<T>())
return &std::get<std::vector<T>>(this->data);
return nullptr;
}
template<typename T>
[[nodiscard]] const std::vector<T>* as_vector() const
{
static_assert(is_nbt_t<T>::value, "Must be NBT type.");
if (this->type() == TagType<T>())
return &std::get<std::vector<T>>(this->data);
return nullptr;
}
// Returns a nullptr upon any kind of failure, including out of range failure.
template<typename T>
[[nodiscard]] T* get(size_t index)
{
static_assert(is_nbt_t<T>::value, "Must be NBT type.");
if (this->type() == TagType<T>())
{
std::vector<T>& ref = std::get<std::vector<T>>(this->data);
if (index < ref.size())
return &ref[index];
}
return nullptr;
}
template<typename T>
[[nodiscard]] const T* get(size_t index)
{
static_assert(is_nbt_t<T>::value, "Must be NBT type.");
if (this->type() == TagType<T>())
{
std::vector<T>& ref = std::get<std::vector<T>>(this->data);
if (index < ref.size())
return &ref[index];
}
return nullptr;
}
// This function is designed to silently fail.
template<typename T>
void set(size_t index, const T& cvalue)
{
static_assert(is_nbt_t<T>::value, "Must be NBT type.");
if (this->type() == TagType<T>())
{
std::vector<T>& ref = std::get<std::vector<T>>(this->data);
if (index < ref.size())
{
ref[index] = cvalue;
}
}
}
// This function is designed to silently fail.
template<typename T>
void set(size_t index, T&& rvalue)
{
static_assert(is_nbt_t<T>::value, "Must be NBT type.");
if (this->type() == TagType<T>())
{
std::vector<T>& ref = std::get<std::vector<T>>(this->data);
if (index < ref.size())
{
ref[index] = rvalue;
}
}
}
template<typename ItT, typename T>
void insert(ItT at, const T& value)
{
static_assert(is_nbt_t<T>::value, "Must be NBT type.");
if (this->type() == TagType<T>())
{
std::vector<T>& ref = std::get<std::vector<T>>(this->data);
// TODO: Finish
ref.insert(at, value);
}
}
template<typename ItT, typename T>
void insert(ItT at, T&& value)
{
static_assert(is_nbt_t<T>::value, "Must be NBT type.");
if (this->type() == TagType<T>())
{
std::vector<T>& ref = std::get<std::vector<T>>(this->data);
// TODO: Finish
ref.insert(at, value);
}
}
template<typename T>
void insert(size_t index, const T& value)
{
static_assert(is_nbt_t<T>::value, "Must be NBT type.");
if (this->type() == TagType<T>())
{
std::vector<T>& ref = std::get<std::vector<T>>(this->data);
if (index > ref.size())
return;
// TODO: Finish
ref.insert(ref.begin() + index, value);
}
}
template<typename T>
void insert(size_t index, T&& value)
{
static_assert(is_nbt_t<T>::value, "Must be NBT type.");
if (this->type() == TagType<T>())
{
std::vector<T>& ref = std::get<std::vector<T>>(this->data);
if (index > ref.size())
return;
// TODO: Finish
ref.insert(ref.begin() + index, value);
}
}
template<typename T>
void push_back(const T& value)
{
static_assert(is_nbt_t<T>::value, "Must be NBT type.");
if (this->type() == TagType<T>())
{
std::vector<T>& ref = std::get<std::vector<T>>(this->data);
ref.push_back(value);
}
}
template<typename T>
void push_back(T&& value)
{
static_assert(is_nbt_t<T>::value, "Must be NBT type.");
if (this->type() == TagType<T>())
{
std::vector<T>& ref = std::get<std::vector<T>>(this->data);
ref.push_back(value);
}
}
void erase(size_t index);
void erase(size_t start, size_t end);
void clear();
[[nodiscard]] size_t size() const;
[[nodiscard]] tag type() const;
}; // t_list
using t_variant = std::variant<
std::nullptr_t, // 0
t_byte, // 1
t_short, // 2
t_int, // 3
t_long, // 4
t_float, // 5
t_double, // 6
t_bytearray, // 7
t_string, // 8
t_list, // 9
t_compound, // 10
t_intarray, // 11
t_longarray // 12
>; // t_variant
class t_compound
{
public:
using map = std::vector<std::pair<std::string, t_variant>>;
map data;
t_compound() = default;
t_compound(const map& cdata) : data(cdata) {}
t_compound(map&& rdata) : data(rdata) {}
t_compound(const t_compound& rhs) = default;
t_compound(t_compound&& rhs) = default;
t_compound& operator=(const map& cdata);
t_compound& operator=(map&& rdata);
t_compound& operator=(const t_compound& rhs) = default;
t_compound& operator=(t_compound&& rhs) = default;
map::iterator operator[](std::string_view key);
map::const_iterator operator[](std::string_view key) const;
[[nodiscard]] size_t size() const;
[[nodiscard]] map::iterator begin()
{
return data.begin();
}
[[nodiscard]] map::iterator end()
{
return data.end();
}
[[nodiscard]] map::const_iterator begin() const
{
return data.begin();
}
[[nodiscard]] map::const_iterator end() const
{
return data.end();
}
void clear();
void remove(std::string_view key);
[[nodiscard]] tag get_type(std::string_view key) const;
[[nodiscard]] map::iterator find(std::string_view key);
[[nodiscard]] map::const_iterator find(std::string_view key) const;
template<typename T>
[[nodiscard]] T* get_if(std::string_view key)
{
static_assert(is_nbt_t<T>::value, "Must be NBT type.");
map::iterator found = this->find(key);
if (found != data.end() && tag(found->second.index()) == TagType<T>())
{
T& result = std::get<T>(found->second);
return &result;
}
return nullptr;
}
template<typename T>
void set(std::string_view key, const T& value)
{
static_assert(is_nbt_t<T>::value, "Must be NBT type.");
map::iterator found = this->find(key);
if (found != this->data.end())
{
found->second = value;
}
else
{
this->data.push_back({ key, value });
}
}
template<typename T>
void set(std::string_view key, T&& value)
{
static_assert(is_nbt_t<T>::value, "Must be NBT type.");
map::iterator found = this->find(key);
if (found != this->data.end())
{
found->second = value;
}
else
{
this->data.push_back({ key, value });
}
}
};
template<typename T>
T cast_t_var(const t_variant& var)
{
static_assert(is_nbt_t<T>::value, "Must be NBT type.");
if (tag(var.index()) == TagType<T>())
return std::get<T>(var);
if constexpr (
std::is_same<T, t_byte>::value ||
std::is_same<T, t_short>::value ||
std::is_same<T, t_int>::value ||
std::is_same<T, t_long>::value ||
std::is_same<T, t_float>::value ||
std::is_same<T, t_double>::value)
{
switch (tag(var.index()))
{
case tag::BYTE:
return static_cast<T>(std::get<t_byte>(var));
case tag::SHORT:
return static_cast<T>(std::get<t_short>(var));
case tag::INT:
return static_cast<T>(std::get<t_int>(var));
case tag::LONG:
return static_cast<T>(std::get<t_long>(var));
case tag::FLOAT:
return static_cast<T>(std::get<t_float>(var));
case tag::DOUBLE:
return static_cast<T>(std::get<t_double>(var));
}
}
return T{};
}
t_compound& t_compound::operator=(const map& cdata)
{
this->data = cdata;
return *this;
}
t_compound& t_compound::operator=(map&& rdata)
{
this->data = rdata;
return *this;
}
t_compound::map::iterator t_compound::operator[](std::string_view key)
{
return this->find(key);
}
t_compound::map::const_iterator t_compound::operator[](std::string_view key) const
{
return this->find(key);
}
size_t t_compound::size() const
{
return this->data.size();
}
void t_compound::clear()
{
this->data.clear();
}
void t_compound::remove(std::string_view key)
{
auto it = this->find(key);
if (it != this->data.end())
this->data.erase(it);
}
tag t_compound::get_type(std::string_view key) const
{
t_compound::map::const_iterator found = this->find(key);
if (found != this->data.end())
return tag(found->second.index());
return tag::NONE;
}
t_compound::map::iterator t_compound::find(std::string_view key)
{
return std::find_if(data.begin(), data.end(), [key](const std::pair<std::string, t_variant>& val) { return val.first == key; });
}
t_compound::map::const_iterator t_compound::find(std::string_view key) const
{
return std::find_if(data.begin(), data.end(), [key](const std::pair<std::string, t_variant>& val) { return val.first == key; });
}
void t_list::erase(size_t index)
{
switch (this->type())
{
case tag::BYTE:
{
std::vector<t_byte>& ref = std::get<std::vector<t_byte>>(this->data);
if (index < ref.size())
ref.erase(ref.begin() + index);
break;
}
case tag::SHORT:
{
std::vector<t_short>& ref = std::get<std::vector<t_short>>(this->data);
if (index < ref.size())
ref.erase(ref.begin() + index);
break;
}
case tag::INT:
{
std::vector<t_int>& ref = std::get<std::vector<t_int>>(this->data);
if (index < ref.size())
ref.erase(ref.begin() + index);
break;
}
case tag::LONG:
{
std::vector<t_long>& ref = std::get<std::vector<t_long>>(this->data);
if (index < ref.size())
ref.erase(ref.begin() + index);
break;
}
case tag::FLOAT:
{
std::vector<t_float>& ref = std::get<std::vector<t_float>>(this->data);
if (index < ref.size())
ref.erase(ref.begin() + index);
break;
}
case tag::DOUBLE:
{
std::vector<t_double>& ref = std::get<std::vector<t_double>>(this->data);
if (index < ref.size())
ref.erase(ref.begin() + index);
break;
}
case tag::BYTEARRAY:
{
std::vector<t_bytearray>& ref = std::get<std::vector<t_bytearray>>(this->data);
if (index < ref.size())
ref.erase(ref.begin() + index);
break;
}
case tag::STRING:
{
std::vector<t_string>& ref = std::get<std::vector<t_string>>(this->data);
if (index < ref.size())
ref.erase(ref.begin() + index);
break;
}
case tag::LIST:
{
std::vector<t_list>& ref = std::get<std::vector<t_list>>(this->data);
if (index < ref.size())
ref.erase(ref.begin() + index);
break;
}
case tag::COMPOUND:
{
std::vector<t_compound>& ref = std::get<std::vector<t_compound>>(this->data);
if (index < ref.size())
ref.erase(ref.begin() + index);
break;
}
case tag::INTARRAY:
{
std::vector<t_intarray>& ref = std::get<std::vector<t_intarray>>(this->data);
if (index < ref.size())
ref.erase(ref.begin() + index);
break;
}
case tag::LONGARRAY:
{
std::vector<t_longarray>& ref = std::get<std::vector<t_longarray>>(this->data);
if (index < ref.size())
ref.erase(ref.begin() + index);
break;
}
}
}
void t_list::erase(size_t start, size_t end)
{
switch (this->type())
{
case tag::BYTE:
{
std::vector<t_byte>& ref = std::get<std::vector<t_byte>>(this->data);
if (start < ref.size() && end > start && end <= ref.size())
ref.erase(ref.begin() + start, ref.begin() + end);
break;
}
case tag::SHORT:
{
std::vector<t_short>& ref = std::get<std::vector<t_short>>(this->data);
if (start < ref.size() && end > start && end <= ref.size())
ref.erase(ref.begin() + start, ref.begin() + end);
break;
}
case tag::INT:
{
std::vector<t_int>& ref = std::get<std::vector<t_int>>(this->data);
if (start < ref.size() && end > start && end <= ref.size())
ref.erase(ref.begin() + start, ref.begin() + end);
break;
}
case tag::LONG:
{
std::vector<t_long>& ref = std::get<std::vector<t_long>>(this->data);
if (start < ref.size() && end > start && end <= ref.size())
ref.erase(ref.begin() + start, ref.begin() + end);
break;
}
case tag::FLOAT:
{
std::vector<t_float>& ref = std::get<std::vector<t_float>>(this->data);
if (start < ref.size() && end > start && end <= ref.size())
ref.erase(ref.begin() + start, ref.begin() + end);
break;
}
case tag::DOUBLE:
{
std::vector<t_double>& ref = std::get<std::vector<t_double>>(this->data);
if (start < ref.size() && end > start && end <= ref.size())
ref.erase(ref.begin() + start, ref.begin() + end);
break;
}
case tag::BYTEARRAY:
{
std::vector<t_bytearray>& ref = std::get<std::vector<t_bytearray>>(this->data);
if (start < ref.size() && end > start && end <= ref.size())
ref.erase(ref.begin() + start, ref.begin() + end);
break;
}
case tag::STRING:
{
std::vector<t_string>& ref = std::get<std::vector<t_string>>(this->data);
if (start < ref.size() && end > start && end <= ref.size())
ref.erase(ref.begin() + start, ref.begin() + end);
break;
}
case tag::LIST:
{
std::vector<t_list>& ref = std::get<std::vector<t_list>>(this->data);
if (start < ref.size() && end > start && end <= ref.size())
ref.erase(ref.begin() + start, ref.begin() + end);
break;
}
case tag::COMPOUND:
{
std::vector<t_compound>& ref = std::get<std::vector<t_compound>>(this->data);
if (start < ref.size() && end > start && end <= ref.size())
ref.erase(ref.begin() + start, ref.begin() + end);
break;
}
case tag::INTARRAY:
{
std::vector<t_intarray>& ref = std::get<std::vector<t_intarray>>(this->data);
if (start < ref.size() && end > start && end <= ref.size())
ref.erase(ref.begin() + start, ref.begin() + end);
break;
}
case tag::LONGARRAY:
{
std::vector<t_longarray>& ref = std::get<std::vector<t_longarray>>(this->data);
if (start < ref.size() && end > start && end <= ref.size())
ref.erase(ref.begin() + start, ref.begin() + end);
break;
}
}
}
void t_list::clear()
{
this->data = nullptr;
}
tag t_list::type() const
{
return tag(this->data.index());
}
size_t t_list::size() const
{
switch (this->type())
{
case tag::NONE:
// Since the type is None, the length is 0.
return 0;
case tag::BYTE:
return std::get<std::vector<t_byte>>(this->data).size();
case tag::SHORT:
return std::get<std::vector<t_short>>(this->data).size();
case tag::INT:
return std::get<std::vector<t_int>>(this->data).size();
case tag::LONG:
return std::get<std::vector<t_long>>(this->data).size();
case tag::FLOAT:
return std::get<std::vector<t_float>>(this->data).size();
case tag::DOUBLE:
return std::get<std::vector<t_double>>(this->data).size();
case tag::BYTEARRAY:
return std::get<std::vector<t_bytearray>>(this->data).size();
case tag::STRING:
return std::get<std::vector<t_string>>(this->data).size();
case tag::LIST:
return std::get<std::vector<t_list>>(this->data).size();
case tag::COMPOUND:
return std::get<std::vector<t_compound>>(this->data).size();
case tag::INTARRAY:
return std::get<std::vector<t_intarray>>(this->data).size();
case tag::LONGARRAY:
return std::get<std::vector<t_longarray>>(this->data).size();
}
return 0;
}
class nbtin
{
public:
const std::byte* begin;
const std::byte* end;
const std::byte* scan;
nbtin(const std::vector<std::byte>& buffer) : begin(&*buffer.begin()), end(&*buffer.begin() + buffer.size()), scan(&*buffer.begin()) {}
nbtin(const void* begin, const void* end)
: begin(static_cast<const std::byte*>(begin)),
end(static_cast<const std::byte*>(end)),
scan(static_cast<const std::byte*>(begin)) {}
nbtin(const void* begin, size_t size)
: begin(static_cast<const std::byte*>(begin)),
end(static_cast<const std::byte*>(begin) + size),
scan(static_cast<const std::byte*>(begin)) {}
nbtin(const nbtin& cvalue) = default;
nbtin(nbtin&& rvalue) = delete;
nbtin& operator=(const nbtin& cvalue) = default;
nbtin& operator=(nbtin&& rvalue) = delete;
[[nodiscard]] inline size_t tellg() const
{
return scan - begin;
}
[[nodiscard]] inline size_t size() const
{
return end - begin;
}
[[nodiscard]] inline bool ensure(size_t count) const
{
return scan + count <= end;
}
inline void advance(size_t count)
{
scan += count;
}
[[nodiscard]] inline bool good() const
{
return scan < end;
}
nbtin& seekg(size_t count, std::ios_base::seekdir dir = std::ios_base::beg)
{
switch (dir)
{
case std::ios_base::beg:
scan = begin + count;
break;
case std::ios_base::cur:
scan = scan + count;
break;
case std::ios_base::end:
scan = end + count;
break;
}
return *this;
}
[[nodiscard]] inline uint8_t read_u8()
{
if (!ensure(1))
throw std::runtime_error("Reached end of buffer.");
uint8_t result = static_cast<uint8_t>(*scan);
advance(1);
return result;
}
[[nodiscard]] inline int8_t read_i8()
{
if (!ensure(1))
throw std::runtime_error("Reached end of buffer.");
int8_t result = static_cast<int8_t>(*scan);
advance(1);
return result;
}
[[nodiscard]] inline uint16_t read_u16()
{
if (!ensure(2))
throw std::runtime_error("Reached end of buffer.");
uint16_t result = static_cast<uint16_t>(scan[0]) << 8 | static_cast<uint16_t>(scan[1]);
advance(2);
return result;
}
[[nodiscard]] inline int16_t read_i16()
{
if (!ensure(2))
throw std::runtime_error("Reached end of buffer.");
int16_t result = static_cast<int16_t>(scan[0]) << 8 | static_cast<int8_t>(scan[1]);
advance(2);
return result;
}
[[nodiscard]] inline uint32_t read_u32()
{
if (!ensure(4))
throw std::runtime_error("Reached end of buffer.");
uint32_t result =
static_cast<uint32_t>(scan[0]) << 24 |
static_cast<uint32_t>(scan[1]) << 16 |
static_cast<uint32_t>(scan[2]) << 8 |
static_cast<uint32_t>(scan[3]);
advance(4);
return result;
}
[[nodiscard]] inline int32_t read_i32()
{
return static_cast<int32_t>(read_u32());
}
[[nodiscard]] inline uint64_t read_u64()
{
if (!ensure(8))
throw std::runtime_error("Reached end of buffer.");
uint64_t result =
static_cast<uint64_t>(scan[0]) << 56 |
static_cast<uint64_t>(scan[1]) << 48 |
static_cast<uint64_t>(scan[2]) << 40 |
static_cast<uint64_t>(scan[3]) << 32 |
static_cast<uint64_t>(scan[4]) << 24 |
static_cast<uint64_t>(scan[5]) << 16 |
static_cast<uint64_t>(scan[6]) << 8 |
static_cast<uint64_t>(scan[7]);
advance(8);
return result;
}
[[nodiscard]] inline int64_t read_i64()
{
return static_cast<int64_t>(read_u64());
}
[[nodiscard]] inline float read_f32()
{
const auto as_int = read_u32();
float result;
std::memcpy(&result, &as_int, 4);
return result;
}
[[nodiscard]] inline double read_f64()
{
const auto as_int = read_u64();
double result;
std::memcpy(&result, &as_int, 8);
return result;
}
[[nodiscard]] inline std::string read_str()
{
const auto length = read_u16();
if (!ensure(length))
throw std::runtime_error("Reached end of buffer.");
std::string result(
reinterpret_cast<const char*>(scan),
reinterpret_cast<const char*>(scan + length));
advance(length);
return result;
}
[[nodiscard]] inline tag read_type()
{
const auto val = read_u8();
return tag(val);
}
};
class nbtout
{
public:
std::vector<std::byte> buffer;
nbtout() { buffer.reserve(64 * 1024); }
nbtout(size_t reservation) { buffer.reserve(reservation); }
nbtout(const std::vector<std::byte>& cvalue) : buffer(cvalue) {}
nbtout(std::vector<std::byte>&& rvalue) : buffer(rvalue) {}
nbtout(const nbtout& cvalue) = default;
nbtout(nbtout&& rvalue) = default;
nbtout& operator=(const nbtout& cvalue) = default;
nbtout& operator=(nbtout&& rvalue) = default;
[[nodiscard]] inline size_t size() const
{
return buffer.size();
}
inline void write(std::byte value)
{
buffer.push_back(value);
}
inline void write(uint8_t value)
{
buffer.push_back(static_cast<std::byte>(value));
}
inline void write(int8_t value)
{
buffer.push_back(static_cast<std::byte>(value));
}
inline void write(uint16_t value)
{
buffer.push_back(static_cast<std::byte>((value >> 8) & 0xFF));
buffer.push_back(static_cast<std::byte>(value & 0xFF));
}
inline void write(int16_t value)
{
buffer.push_back(static_cast<std::byte>((value >> 8) & 0xFF));
buffer.push_back(static_cast<std::byte>(value & 0xFF));
}
inline void write(uint32_t value)
{
buffer.push_back(static_cast<std::byte>((value >> 24) & 0xFF));
buffer.push_back(static_cast<std::byte>((value >> 16) & 0xFF));
buffer.push_back(static_cast<std::byte>((value >> 8) & 0xFF));
buffer.push_back(static_cast<std::byte>((value) & 0xFF));
}
inline void write(int32_t value)
{
buffer.push_back(static_cast<std::byte>((value >> 24) & 0xFF));
buffer.push_back(static_cast<std::byte>((value >> 16) & 0xFF));
buffer.push_back(static_cast<std::byte>((value >> 8) & 0xFF));
buffer.push_back(static_cast<std::byte>((value) & 0xFF));
}
inline void write(uint64_t value)
{
buffer.push_back(static_cast<std::byte>((value >> 56) & 0xFF));
buffer.push_back(static_cast<std::byte>((value >> 48) & 0xFF));
buffer.push_back(static_cast<std::byte>((value >> 40) & 0xFF));
buffer.push_back(static_cast<std::byte>((value >> 32) & 0xFF));
buffer.push_back(static_cast<std::byte>((value >> 24) & 0xFF));
buffer.push_back(static_cast<std::byte>((value >> 16) & 0xFF));
buffer.push_back(static_cast<std::byte>((value >> 8) & 0xFF));
buffer.push_back(static_cast<std::byte>((value) & 0xFF));
}
inline void write(int64_t value)
{
buffer.push_back(static_cast<std::byte>((value >> 56) & 0xFF));
buffer.push_back(static_cast<std::byte>((value >> 48) & 0xFF));
buffer.push_back(static_cast<std::byte>((value >> 40) & 0xFF));
buffer.push_back(static_cast<std::byte>((value >> 32) & 0xFF));
buffer.push_back(static_cast<std::byte>((value >> 24) & 0xFF));
buffer.push_back(static_cast<std::byte>((value >> 16) & 0xFF));
buffer.push_back(static_cast<std::byte>((value >> 8) & 0xFF));
buffer.push_back(static_cast<std::byte>((value) & 0xFF));
}
inline void write(float value)
{
uint32_t as_int;
std::memcpy(&as_int, &value, 4);
write(as_int);
}
inline void write(double value)
{
uint64_t as_int;
std::memcpy(&as_int, &value, 8);
write(as_int);
}
inline void write(std::string_view value)
{
uint16_t length = value.size();
write(length);
const char* beg = value.data();
buffer.insert(buffer.end(),
reinterpret_cast<const std::byte*>(beg),
reinterpret_cast<const std::byte*>(beg + length));
}
inline void write(tag value)
{
write(static_cast<uint8_t>(value));
}
};
enum class proxytype : uint8_t
{
NONE = 0,
BYTE = 1,
SHORT = 2,
INT = 3,
LONG = 4,
FLOAT = 5,
DOUBLE = 6,
BYTEARRAY = 7,
STRING = 8,
LIST = 9,
COMPOUND = 10,
INTARRAY = 11,
LONGARRAY = 12,
PAIR = 13 // If the type is PAIR, that means that it is a std::pair<std::string, t_variant>
};
class Tag
{
private:
void* ptr;
proxytype _type;
public:
proxytype type() const
{
return _type;
}
using pair = std::pair<std::string, t_variant>;
Tag() : ptr(nullptr), _type(proxytype::NONE) {}
Tag(std::byte& value) : ptr(&value), _type(proxytype::BYTE) {}
Tag(t_byte& value) : ptr(&value), _type(proxytype::BYTE) {}
Tag(t_short& value) : ptr(&value), _type(proxytype::SHORT) {}
Tag(t_int& value) : ptr(&value), _type(proxytype::INT) {}
Tag(t_long& value) : ptr(&value), _type(proxytype::LONG) {}
Tag(t_float& value) : ptr(&value), _type(proxytype::FLOAT) {}
Tag(t_double& value) : ptr(&value), _type(proxytype::DOUBLE) {}
Tag(t_bytearray& value) : ptr(&value), _type(proxytype::BYTEARRAY) {}
Tag(t_string& value) : ptr(&value), _type(proxytype::STRING) {}
Tag(t_list& value) : ptr(&value), _type(proxytype::LIST) {}
Tag(t_compound& value) : ptr(&value), _type(proxytype::COMPOUND) {}
Tag(t_intarray& value) : ptr(&value), _type(proxytype::INTARRAY) {}
Tag(t_longarray& value) : ptr(&value), _type(proxytype::LONGARRAY) {}
Tag(pair& value) : ptr(&value), _type(proxytype::PAIR) {}
Tag(t_variant& value)
{
_type = proxytype(value.index());
switch (_type)
{
case proxytype::BYTE:
{
auto& ref = std::get<t_byte>(value);
ptr = &ref;
break;
}
case proxytype::SHORT:
{
auto& ref = std::get<t_short>(value);
ptr = &ref;
break;
}
case proxytype::INT:
{
auto& ref = std::get<t_int>(value);
ptr = &ref;
break;
}
case proxytype::LONG:
{
auto& ref = std::get<t_long>(value);
ptr = &ref;
break;
}
case proxytype::FLOAT:
{
auto& ref = std::get<t_float>(value);
ptr = &ref;
break;
}
case proxytype::DOUBLE:
{
auto& ref = std::get<t_double>(value);
ptr = &ref;
break;
}
case proxytype::BYTEARRAY:
{
auto& ref = std::get<t_bytearray>(value);
ptr = &ref;
break;
}
case proxytype::STRING:
{
auto& ref = std::get<t_string>(value);
ptr = &ref;
break;
}
case proxytype::LIST:
{
auto& ref = std::get<t_list>(value);
ptr = &ref;
break;
}
case proxytype::COMPOUND:
{
auto& ref = std::get<t_compound>(value);
ptr = &ref;
break;
}
case proxytype::INTARRAY:
{
auto& ref = std::get<t_intarray>(value);
ptr = &ref;
break;
}
case proxytype::LONGARRAY:
{
auto& ref = std::get<t_longarray>(value);
ptr = &ref;
break;
}
default:
ptr = nullptr;
_type = proxytype::NONE;
break;
}
}
// This function will operate on the follow types:
// t_bytearray
// t_intarray
// t_longarray
// t_list
[[nodiscard]] inline Tag operator[](size_t index)
{
switch (_type)
{
case proxytype::BYTEARRAY:
{
t_bytearray* p = static_cast<t_bytearray*>(ptr);
if (index < p->size())
return Tag(p->at(index));
break;
}
case proxytype::INTARRAY:
{
t_intarray* p = static_cast<t_intarray*>(ptr);
if (index < p->size())
return Tag(p->at(index));
break;
}
case proxytype::LONGARRAY:
{
t_longarray* p = static_cast<t_longarray*>(ptr);
if (index < p->size())
return Tag(p->at(index));
break;
}
case proxytype::PAIR:
{
pair* p = static_cast<pair*>(ptr);
// We can create a temporary tag that takes the t_variant
// pointed to by p->second.
// This allows us to do tmp[index] to get our final tag.
// Aren't shortcuts amazing?
Tag tmp(p->second);
return tmp[index];
}
case proxytype::LIST:
{
t_list* p = static_cast<t_list*>(ptr);
switch (p->type())
{
case tag::BYTE:
{
auto& ref = std::get<std::vector<t_byte>>(p->data);
if (index < ref.size())
return Tag(ref.at(index));
break;
}
case tag::SHORT:
{
auto& ref = std::get<std::vector<t_short>>(p->data);
if (index < ref.size())
return Tag(ref.at(index));
break;
}
case tag::INT:
{
auto& ref = std::get<std::vector<t_int>>(p->data);
if (index < ref.size())
return Tag(ref.at(index));
break;
}
case tag::LONG:
{
auto& ref = std::get<std::vector<t_long>>(p->data);
if (index < ref.size())
return Tag(ref.at(index));
break;
}
case tag::FLOAT:
{
auto& ref = std::get<std::vector<t_float>>(p->data);
if (index < ref.size())
return Tag(ref.at(index));
break;
}
case tag::DOUBLE:
{
auto& ref = std::get<std::vector<t_double>>(p->data);
if (index < ref.size())
return Tag(ref.at(index));
break;
}
case tag::BYTEARRAY:
{
auto& ref = std::get<std::vector<t_bytearray>>(p->data);
if (index < ref.size())
return Tag(ref.at(index));
break;
}
case tag::STRING:
{
auto& ref = std::get<std::vector<t_string>>(p->data);
if (index < ref.size())
return Tag(ref.at(index));
break;
}
case tag::LIST:
{
auto& ref = std::get<std::vector<t_list>>(p->data);
if (index < ref.size())
return Tag(ref.at(index));
break;
}
case tag::COMPOUND:
{
auto& ref = std::get<std::vector<t_compound>>(p->data);
if (index < ref.size())
return Tag(ref.at(index));
break;
}
case tag::INTARRAY:
{
auto& ref = std::get<std::vector<t_intarray>>(p->data);
if (index < ref.size())
return Tag(ref.at(index));
break;
}
case tag::LONGARRAY:
{
auto& ref = std::get<std::vector<t_longarray>>(p->data);
if (index < ref.size())
return Tag(ref.at(index));
break;
}
}
break;
}
}
return Tag();
}
[[nodiscard]] inline Tag operator[](const char* key)
{
return this->operator[](std::string_view(key));
}
// This function will operate on compound tags.
[[nodiscard]] inline Tag operator[](std::string_view key)
{
if (_type == proxytype::COMPOUND)
{
t_compound* p = static_cast<t_compound*>(ptr);
auto it = p->find(key);
if (it != p->end())
{
return Tag(*it);
}
}
else if (_type == proxytype::PAIR)
{
pair* p = static_cast<pair*>(ptr);
Tag tmp(p->second);
return tmp[key];
}
return Tag();
}
[[nodiscard]] inline size_t size() const
{
switch (_type)
{
case proxytype::BYTEARRAY:
{
t_bytearray* p = static_cast<t_bytearray*>(ptr);
return p->size();
}
case proxytype::INTARRAY:
{
t_intarray* p = static_cast<t_intarray*>(ptr);
return p->size();
}
case proxytype::LONGARRAY:
{
t_longarray* p = static_cast<t_longarray*>(ptr);
return p->size();
}
case proxytype::PAIR:
{
pair* p = static_cast<pair*>(ptr);
Tag tmp(p->second);
return tmp.size();
}
case proxytype::COMPOUND:
{
t_compound* p = static_cast<t_compound*>(ptr);
return p->data.size();
}
case proxytype::LIST:
{
t_list* p = static_cast<t_list*>(ptr);
switch (p->type())
{
case tag::BYTE:
{
auto& ref = std::get<std::vector<t_byte>>(p->data);
return ref.size();
}
case tag::SHORT:
{
auto& ref = std::get<std::vector<t_short>>(p->data);
return ref.size();
}
case tag::INT:
{
auto& ref = std::get<std::vector<t_int>>(p->data);
return ref.size();
}
case tag::LONG:
{
auto& ref = std::get<std::vector<t_long>>(p->data);
return ref.size();
}
case tag::FLOAT:
{
auto& ref = std::get<std::vector<t_float>>(p->data);
return ref.size();
}
case tag::DOUBLE:
{
auto& ref = std::get<std::vector<t_double>>(p->data);
return ref.size();
}
case tag::BYTEARRAY:
{
auto& ref = std::get<std::vector<t_bytearray>>(p->data);
return ref.size();
}
case tag::STRING:
{
auto& ref = std::get<std::vector<t_string>>(p->data);
return ref.size();
}
case tag::LIST:
{
auto& ref = std::get<std::vector<t_list>>(p->data);
return ref.size();
}
case tag::COMPOUND:
{
auto& ref = std::get<std::vector<t_compound>>(p->data);
return ref.size();
}
case tag::INTARRAY:
{
auto& ref = std::get<std::vector<t_intarray>>(p->data);
return ref.size();
}
case tag::LONGARRAY:
{
auto& ref = std::get<std::vector<t_longarray>>(p->data);
return ref.size();
}
}
break;
}
}
return 0;
}
/// <summary>
/// The name (if any) of this Tag.
/// </summary>
/// <returns>Name of tag or empty string.</returns>
[[nodiscard]] inline std::string_view name() const
{
if (_type == proxytype::PAIR)
{
pair* p = static_cast<pair*>(ptr);
return p->first;
}
return std::string_view();
}
/// <summary>
/// Checks whether this Tag is valid.
/// </summary>
[[nodiscard]] inline operator bool() const
{
return ptr != nullptr && _type != proxytype::NONE;
}
[[nodiscard]] inline operator t_byte() const
{
switch (_type)
{
case proxytype::BYTE:
{
t_byte* p = static_cast<t_byte*>(ptr);
return *p;
}
case proxytype::SHORT:
{
t_short* p = static_cast<t_short*>(ptr);
return static_cast<t_byte>(*p);
}
case proxytype::INT:
{
t_int* p = static_cast<t_int*>(ptr);
return static_cast<t_byte>(*p);
}
case proxytype::FLOAT:
{
t_float* p = static_cast<t_float*>(ptr);
return static_cast<t_byte>(*p);
}
case proxytype::DOUBLE:
{
t_double* p = static_cast<t_double*>(ptr);
return static_cast<t_byte>(*p);
}
case proxytype::PAIR:
{
pair* p = static_cast<pair*>(ptr);
return cast_t_var<t_byte>(p->second);
}
}
return t_byte{};
}
[[nodiscard]] inline operator t_short() const
{
switch (_type)
{
case proxytype::BYTE:
{
std::byte* p = static_cast<std::byte*>(ptr);
return static_cast<t_short>(*p);
}
case proxytype::SHORT:
{
t_short* p = static_cast<t_short*>(ptr);
return static_cast<t_short>(*p);
}
case proxytype::INT:
{
t_int* p = static_cast<t_int*>(ptr);
return static_cast<t_short>(*p);
}
case proxytype::FLOAT:
{
t_float* p = static_cast<t_float*>(ptr);
return static_cast<t_short>(*p);
}
case proxytype::DOUBLE:
{
t_double* p = static_cast<t_double*>(ptr);
return static_cast<t_short>(*p);
}
case proxytype::PAIR:
{
pair* p = static_cast<pair*>(ptr);
return cast_t_var<t_short>(p->second);
}
}
return t_short{};
}
[[nodiscard]] inline operator t_int() const
{
switch (_type)
{
case proxytype::BYTE:
{
std::byte* p = static_cast<std::byte*>(ptr);
return static_cast<t_int>(*p);
}
case proxytype::SHORT:
{
t_short* p = static_cast<t_short*>(ptr);
return static_cast<t_int>(*p);
}
case proxytype::INT:
{
t_int* p = static_cast<t_int*>(ptr);
return static_cast<t_int>(*p);
}
case proxytype::FLOAT:
{
t_float* p = static_cast<t_float*>(ptr);
return static_cast<t_int>(*p);
}
case proxytype::DOUBLE:
{
t_double* p = static_cast<t_double*>(ptr);
return static_cast<t_int>(*p);
}
case proxytype::PAIR:
{
pair* p = static_cast<pair*>(ptr);
return cast_t_var<t_int>(p->second);
}
}
return t_int{};
}
[[nodiscard]] inline operator t_long() const
{
switch (_type)
{
case proxytype::BYTE:
{
std::byte* p = static_cast<std::byte*>(ptr);
return static_cast<t_long>(*p);
}
case proxytype::SHORT:
{
t_short* p = static_cast<t_short*>(ptr);
return static_cast<t_long>(*p);
}
case proxytype::INT:
{
t_int* p = static_cast<t_int*>(ptr);
return static_cast<t_long>(*p);
}
case proxytype::FLOAT:
{
t_float* p = static_cast<t_float*>(ptr);
return static_cast<t_long>(*p);
}
case proxytype::DOUBLE:
{
t_double* p = static_cast<t_double*>(ptr);
return static_cast<t_long>(*p);
}
case proxytype::PAIR:
{
pair* p = static_cast<pair*>(ptr);
return cast_t_var<t_long>(p->second);
}
}
return t_long{};
}
[[nodiscard]] inline operator t_float() const
{
switch (_type)
{
case proxytype::BYTE:
{
std::byte* p = static_cast<std::byte*>(ptr);
return static_cast<t_float>(*p);
}
case proxytype::SHORT:
{
t_short* p = static_cast<t_short*>(ptr);
return static_cast<t_float>(*p);
}
case proxytype::INT:
{
t_int* p = static_cast<t_int*>(ptr);
return static_cast<t_float>(*p);
}
case proxytype::FLOAT:
{
t_float* p = static_cast<t_float*>(ptr);
return static_cast<t_float>(*p);
}
case proxytype::DOUBLE:
{
t_double* p = static_cast<t_double*>(ptr);
return static_cast<t_float>(*p);
}
case proxytype::PAIR:
{
pair* p = static_cast<pair*>(ptr);
return cast_t_var<t_float>(p->second);
}
}
return t_float{};
}
[[nodiscard]] inline operator t_double() const
{
switch (_type)
{
case proxytype::BYTE:
{
std::byte* p = static_cast<std::byte*>(ptr);
return static_cast<t_double>(*p);
}
case proxytype::SHORT:
{
t_short* p = static_cast<t_short*>(ptr);
return static_cast<t_double>(*p);
}
case proxytype::INT:
{
t_int* p = static_cast<t_int*>(ptr);
return static_cast<t_double>(*p);
}
case proxytype::FLOAT:
{
t_float* p = static_cast<t_float*>(ptr);
return static_cast<t_double>(*p);
}
case proxytype::DOUBLE:
{
t_double* p = static_cast<t_double*>(ptr);
return static_cast<t_double>(*p);
}
case proxytype::PAIR:
{
pair* p = static_cast<pair*>(ptr);
return cast_t_var<t_double>(p->second);
}
}
return t_double{};
}
[[nodiscard]] inline operator std::string_view() const
{
if (_type == proxytype::STRING)
{
std::string* p = static_cast<std::string*>(ptr);
return *p;
}
else if (_type == proxytype::PAIR)
{
pair* p = static_cast<pair*>(ptr);
if (tag(p->second.index()) == tag::STRING)
{
return std::get<std::string>(p->second);
}
}
return std::string_view();
}
[[nodiscard]] inline operator std::string() const
{
if (_type == proxytype::STRING)
{
std::string* p = static_cast<std::string*>(ptr);
return *p;
}
else if (_type == proxytype::PAIR)
{
pair* p = static_cast<pair*>(ptr);
if (tag(p->second.index()) == tag::STRING)
{
return std::get<std::string>(p->second);
}
}
return std::string();
}
inline Tag& operator=(std::byte value)
{
switch (_type)
{
case proxytype::BYTE:
{
std::byte* p = static_cast<std::byte*>(ptr);
*p = value;
break;
}
case proxytype::SHORT:
{
t_short* p = static_cast<t_short*>(ptr);
*p = static_cast<t_short>(value);
break;
}
case proxytype::INT:
{
t_int* p = static_cast<t_int*>(ptr);
*p = static_cast<t_int>(value);
break;
}
case proxytype::FLOAT:
{
t_float* p = static_cast<t_float*>(ptr);
*p = static_cast<float>(value);
break;
}
case proxytype::DOUBLE:
{
t_double* p = static_cast<t_double*>(ptr);
*p = static_cast<double>(value);
break;
}
case proxytype::PAIR:
{
pair* p = static_cast<pair*>(ptr);
p->second = static_cast<t_byte>(value);
break;
}
}
return *this;
}
inline Tag& operator=(t_byte value)
{
switch (_type)
{
case proxytype::BYTE:
{
t_byte* p = static_cast<t_byte*>(ptr);
*p = static_cast<t_byte>(value);
break;
}
case proxytype::SHORT:
{
t_short* p = static_cast<t_short*>(ptr);
*p = static_cast<t_short>(value);
break;
}
case proxytype::INT:
{
t_int* p = static_cast<t_int*>(ptr);
*p = static_cast<t_int>(value);
break;
}
case proxytype::FLOAT:
{
t_float* p = static_cast<t_float*>(ptr);
*p = static_cast<float>(value);
break;
}
case proxytype::DOUBLE:
{
t_double* p = static_cast<t_double*>(ptr);
*p = static_cast<double>(value);
break;
}
case proxytype::PAIR:
{
pair* p = static_cast<pair*>(ptr);
p->second = value;
break;
}
}
return *this;
}
inline Tag& operator=(t_short value)
{
switch (_type)
{
case proxytype::BYTE:
{
t_byte* p = static_cast<t_byte*>(ptr);
*p = static_cast<t_byte>(value);
break;
}
case proxytype::SHORT:
{
t_short* p = static_cast<t_short*>(ptr);
*p = static_cast<t_short>(value);
break;
}
case proxytype::INT:
{
t_int* p = static_cast<t_int*>(ptr);
*p = static_cast<t_int>(value);
break;
}
case proxytype::FLOAT:
{
t_float* p = static_cast<t_float*>(ptr);
*p = static_cast<float>(value);
break;
}
case proxytype::DOUBLE:
{
t_double* p = static_cast<t_double*>(ptr);
*p = static_cast<double>(value);
break;
}
case proxytype::PAIR:
{
pair* p = static_cast<pair*>(ptr);
p->second = value;
break;
}
}
return *this;
}
inline Tag& operator=(t_int value)
{
switch (_type)
{
case proxytype::BYTE:
{
t_byte* p = static_cast<t_byte*>(ptr);
*p = static_cast<t_byte>(value);
break;
}
case proxytype::SHORT:
{
t_short* p = static_cast<t_short*>(ptr);
*p = static_cast<t_short>(value);
break;
}
case proxytype::INT:
{
t_int* p = static_cast<t_int*>(ptr);
*p = static_cast<t_int>(value);
break;
}
case proxytype::FLOAT:
{
t_float* p = static_cast<t_float*>(ptr);
*p = static_cast<float>(value);
break;
}
case proxytype::DOUBLE:
{
t_double* p = static_cast<t_double*>(ptr);
*p = static_cast<double>(value);
break;
}
case proxytype::PAIR:
{
pair* p = static_cast<pair*>(ptr);
p->second = value;
break;
}
}
return *this;
}
inline Tag& operator=(t_long value)
{
{
switch (_type)
{
case proxytype::BYTE:
{
t_byte* p = static_cast<t_byte*>(ptr);
*p = static_cast<t_byte>(value);
break;
}
case proxytype::SHORT:
{
t_short* p = static_cast<t_short*>(ptr);
*p = static_cast<t_short>(value);
break;
}
case proxytype::INT:
{
t_int* p = static_cast<t_int*>(ptr);
*p = static_cast<t_int>(value);
break;
}
case proxytype::FLOAT:
{
t_float* p = static_cast<t_float*>(ptr);
*p = static_cast<float>(value);
break;
}
case proxytype::DOUBLE:
{
t_double* p = static_cast<t_double*>(ptr);
*p = static_cast<double>(value);
break;
}
case proxytype::PAIR:
{
pair* p = static_cast<pair*>(ptr);
p->second = value;
break;
}
}
return *this;
}
}
inline Tag& operator=(t_float value)
{
switch (_type)
{
case proxytype::BYTE:
{
t_byte* p = static_cast<t_byte*>(ptr);
*p = static_cast<t_byte>(value);
break;
}
case proxytype::SHORT:
{
t_short* p = static_cast<t_short*>(ptr);
*p = static_cast<t_short>(value);
break;
}
case proxytype::INT:
{
t_int* p = static_cast<t_int*>(ptr);
*p = static_cast<t_int>(value);
break;
}
case proxytype::FLOAT:
{
t_float* p = static_cast<t_float*>(ptr);
*p = static_cast<float>(value);
break;
}
case proxytype::DOUBLE:
{
t_double* p = static_cast<t_double*>(ptr);
*p = static_cast<double>(value);
break;
}
case proxytype::PAIR:
{
pair* p = static_cast<pair*>(ptr);
p->second = value;
break;
}
}
return *this;
}
inline Tag& operator=(t_double value)
{
switch (_type)
{
case proxytype::BYTE:
{
t_byte* p = static_cast<t_byte*>(ptr);
*p = static_cast<t_byte>(value);
break;
}
case proxytype::SHORT:
{
t_short* p = static_cast<t_short*>(ptr);
*p = static_cast<t_short>(value);
break;
}
case proxytype::INT:
{
t_int* p = static_cast<t_int*>(ptr);
*p = static_cast<t_int>(value);
break;
}
case proxytype::FLOAT:
{
t_float* p = static_cast<t_float*>(ptr);
*p = static_cast<float>(value);
break;
}
case proxytype::DOUBLE:
{
t_double* p = static_cast<t_double*>(ptr);
*p = static_cast<double>(value);
break;
}
case proxytype::PAIR:
{
pair* p = static_cast<pair*>(ptr);
p->second = value;
break;
}
}
return *this;
}
inline Tag& operator=(std::string_view value)
{
if (_type == proxytype::STRING)
{
t_string* p = static_cast<t_string*>(ptr);
*p = value;
}
else if (_type == proxytype::PAIR)
{
pair* p = static_cast<pair*>(ptr);
p->second = std::string(value);
}
return *this;
}
[[nodiscard]] inline bool operator==(std::nullptr_t) const
{
return ptr == nullptr;
}
[[nodiscard]] inline bool operator==(t_byte value) const
{
switch (_type)
{
case proxytype::BYTE:
{
t_byte* p = static_cast<t_byte*>(ptr);
return *p == value;
}
case proxytype::SHORT:
{
t_short* p = static_cast<t_short*>(ptr);
return *p == value;
}
case proxytype::INT:
{
t_int* p = static_cast<t_int*>(ptr);
return *p == value;
}
case proxytype::FLOAT:
{
t_float* p = static_cast<t_float*>(ptr);
return *p == value;
}
case proxytype::DOUBLE:
{
t_double* p = static_cast<t_double*>(ptr);
return *p == value;
}
case proxytype::PAIR:
{
pair* p = static_cast<pair*>(ptr);
return cast_t_var<t_byte>(p->second) == value;
}
}
return false;
}
[[nodiscard]] inline bool operator==(t_short value) const
{
switch (_type)
{
case proxytype::BYTE:
{
t_byte* p = static_cast<t_byte*>(ptr);
return *p == value;
}
case proxytype::SHORT:
{
t_short* p = static_cast<t_short*>(ptr);
return *p == value;
}
case proxytype::INT:
{
t_int* p = static_cast<t_int*>(ptr);
return *p == value;
}
case proxytype::FLOAT:
{
t_float* p = static_cast<t_float*>(ptr);
return *p == value;
}
case proxytype::DOUBLE:
{
t_double* p = static_cast<t_double*>(ptr);
return *p == value;
}
case proxytype::PAIR:
{
pair* p = static_cast<pair*>(ptr);
return cast_t_var<t_short>(p->second) == value;
}
}
return false;
}
[[nodiscard]] inline bool operator==(t_int value) const
{
switch (_type)
{
case proxytype::BYTE:
{
t_byte* p = static_cast<t_byte*>(ptr);
return *p == value;
}
case proxytype::SHORT:
{
t_short* p = static_cast<t_short*>(ptr);
return *p == value;
}
case proxytype::INT:
{
t_int* p = static_cast<t_int*>(ptr);
return *p == value;
}
case proxytype::FLOAT:
{
t_float* p = static_cast<t_float*>(ptr);
return *p == value;
}
case proxytype::DOUBLE:
{
t_double* p = static_cast<t_double*>(ptr);
return *p == value;
}
case proxytype::PAIR:
{
pair* p = static_cast<pair*>(ptr);
return cast_t_var<t_int>(p->second) == value;
}
}
return false;
}
[[nodiscard]] inline bool operator==(t_long value) const
{
switch (_type)
{
case proxytype::BYTE:
{
t_byte* p = static_cast<t_byte*>(ptr);
return *p == value;
}
case proxytype::SHORT:
{
t_short* p = static_cast<t_short*>(ptr);
return *p == value;
}
case proxytype::INT:
{
t_int* p = static_cast<t_int*>(ptr);
return *p == value;
}
case proxytype::FLOAT:
{
t_float* p = static_cast<t_float*>(ptr);
return *p == value;
}
case proxytype::DOUBLE:
{
t_double* p = static_cast<t_double*>(ptr);
return *p == value;
}
case proxytype::PAIR:
{
pair* p = static_cast<pair*>(ptr);
return cast_t_var<t_long>(p->second) == value;
}
}
return false;
}
[[nodiscard]] inline bool operator==(t_float value) const
{
switch (_type)
{
case proxytype::BYTE:
{
t_byte* p = static_cast<t_byte*>(ptr);
return *p == value;
}
case proxytype::SHORT:
{
t_short* p = static_cast<t_short*>(ptr);
return *p == value;
}
case proxytype::INT:
{
t_int* p = static_cast<t_int*>(ptr);
return *p == value;
}
case proxytype::FLOAT:
{
t_float* p = static_cast<t_float*>(ptr);
return *p == value;
}
case proxytype::DOUBLE:
{
t_double* p = static_cast<t_double*>(ptr);
return *p == value;
}
case proxytype::PAIR:
{
pair* p = static_cast<pair*>(ptr);
return cast_t_var<t_float>(p->second) == value;
}
}
return false;
}
[[nodiscard]] inline bool operator==(t_double value) const
{
switch (_type)
{
case proxytype::BYTE:
{
t_byte* p = static_cast<t_byte*>(ptr);
return *p == value;
}
case proxytype::SHORT:
{
t_short* p = static_cast<t_short*>(ptr);
return *p == value;
}
case proxytype::INT:
{
t_int* p = static_cast<t_int*>(ptr);
return *p == value;
}
case proxytype::FLOAT:
{
t_float* p = static_cast<t_float*>(ptr);
return *p == value;
}
case proxytype::DOUBLE:
{
t_double* p = static_cast<t_double*>(ptr);
return *p == value;
}
case proxytype::PAIR:
{
pair* p = static_cast<pair*>(ptr);
return cast_t_var<t_double>(p->second) == value;
}
}
return false;
}
[[nodiscard]] inline bool operator==(std::string_view value) const
{
if (_type == proxytype::STRING)
{
std::string* p = static_cast<std::string*>(ptr);
return *p == value;
}
else if (_type == proxytype::PAIR)
{
pair* p = static_cast<pair*>(ptr);
if (tag(p->second.index()) == tag::STRING)
return std::get<std::string>(p->second) == value;
}
return false;
}
};
class NBTree
{
public:
t_variant root;
Tag root_tag;
std::string name;
NBTree() : root(nullptr) {}
NBTree(const t_variant& value, std::string_view tag_name = "") : root(value), name(tag_name) { root_tag = Tag(root); }
NBTree(t_variant&& value, std::string_view tag_name = "") : root(value), name(tag_name) { root_tag = Tag(root); }
NBTree(t_byte value, std::string_view tag_name = "") : root(value), name(tag_name) { root_tag = Tag(root); }
NBTree(t_short value, std::string_view tag_name = "") : root(value), name(tag_name) { root_tag = Tag(root); }
NBTree(t_int value, std::string_view tag_name = "") : root(value), name(tag_name) { root_tag = Tag(root); }
NBTree(t_long value, std::string_view tag_name = "") : root(value), name(tag_name) { root_tag = Tag(root); }
NBTree(t_float value, std::string_view tag_name = "") : root(value), name(tag_name) { root_tag = Tag(root); }
NBTree(t_double value, std::string_view tag_name = "") : root(value), name(tag_name) { root_tag = Tag(root); }
NBTree(const t_bytearray& value, std::string_view tag_name = "") : root(value), name(tag_name) { root_tag = Tag(root); }
NBTree(t_bytearray&& value, std::string_view tag_name = "") : root(value), name(tag_name) { root_tag = Tag(root); }
NBTree(const t_string& value, std::string_view tag_name = "") : root(value), name(tag_name) { root_tag = Tag(root); }
NBTree(t_string&& value, std::string_view tag_name = "") : root(value), name(tag_name) { root_tag = Tag(root); }
NBTree(const t_list& value, std::string_view tag_name = "") : root(value), name(tag_name) { root_tag = Tag(root); }
NBTree(t_list&& value, std::string_view tag_name = "") : root(value), name(tag_name) { root_tag = Tag(root); }
NBTree(const t_compound& value, std::string_view tag_name = "") : root(value), name(tag_name) { root_tag = Tag(root); }
NBTree(t_compound&& value, std::string_view tag_name = "") : root(value), name(tag_name) { root_tag = Tag(root); }
NBTree(const t_intarray& value, std::string_view tag_name = "") : root(value), name(tag_name) { root_tag = Tag(root); }
NBTree(t_intarray&& value, std::string_view tag_name = "") : root(value), name(tag_name) { root_tag = Tag(root); }
NBTree(const t_longarray& value, std::string_view tag_name = "") : root(value), name(tag_name) { root_tag = Tag(root); }
NBTree(t_longarray&& value, std::string_view tag_name = "") : root(value), name(tag_name) { root_tag = Tag(root); }
NBTree(const NBTree& rhs) = default;
NBTree(NBTree&& rhs) = default;
NBTree& operator=(const NBTree& rhs) = default;
NBTree& operator=(NBTree&& rhs) = default;
[[nodiscard]] inline size_t size() const
{
return this->root_tag.size();
}
// We need to be able to get the tag type easily.
[[nodiscard]] inline tag type()
{
return tag(root.index());
}
template<typename T>
[[nodiscard]] inline T* get_if()
{
static_assert(is_nbt_t<T>::value, "Must be NBT type.");
if (type() == TagType<T>())
return &std::get<T>(root);
return nullptr;
}
[[nodiscard]] inline Tag operator[](size_t index)
{
return this->root_tag[index];
}
[[nodiscard]] inline Tag operator[](const char* key)
{
return this->operator[](std::string_view(key));
}
[[nodiscard]] inline Tag operator[](std::string_view key)
{
return this->root_tag[key];
}
};
t_byte read_byte(nbtin& in);
t_short read_short(nbtin& in);
t_int read_int(nbtin& in);
t_long read_long(nbtin& in);
t_float read_float(nbtin& in);
t_double read_double(nbtin& in);
t_bytearray read_bytearray(nbtin& in);
t_string read_string(nbtin& in);
t_list read_list(nbtin& in);
t_compound read_compound(nbtin& in);
t_intarray read_intarray(nbtin& in);
t_longarray read_longarray(nbtin& in);
t_variant read_tag(nbtin& in, tag type);
[[nodiscard]] inline t_byte read_byte(nbtin& in)
{
return in.read_i8();
}
[[nodiscard]] inline t_short read_short(nbtin& in)
{
return in.read_i16();
}
[[nodiscard]] inline t_int read_int(nbtin& in)
{
return in.read_i32();
}
[[nodiscard]] inline t_long read_long(nbtin& in)
{
return in.read_i64();
}
[[nodiscard]] inline t_float read_float(nbtin& in)
{
return in.read_f32();
}
[[nodiscard]] inline t_double read_double(nbtin& in)
{
return in.read_f64();
}
[[nodiscard]] inline t_string read_string(nbtin& in)
{
return in.read_str();
}
[[nodiscard]] inline t_bytearray read_bytearray(nbtin& in)
{
int length = in.read_i32();
if (!in.ensure(length))
throw std::runtime_error("Reached end of buffer.");
t_bytearray result;
result.reserve(length);
for (int i = 0; i < length; i++)
{
result.push_back(static_cast<std::byte>(in.read_u8()));
}
return result;
}
[[nodiscard]] inline t_intarray read_intarray(nbtin& in)
{
int length = in.read_i32();
if (!in.ensure(length * 4))
throw std::runtime_error("Reached end of buffer.");
t_intarray result;
result.reserve(length);
for (int i = 0; i < length; i++)
{
result.push_back(in.read_i32());
}
return result;
}
[[nodiscard]] inline t_longarray read_longarray(nbtin& in)
{
int length = in.read_i32();
if (!in.ensure(length * 8))
throw std::runtime_error("Reached end of buffer.");
t_longarray result;
result.reserve(length);
for (int i = 0; i < length; i++)
{
result.push_back(in.read_i64());
}
return result;
}
[[nodiscard]] inline t_list read_list(nbtin& in)
{
tag list_type = in.read_type();
int length = in.read_i32();
if (length == 0 || list_type == tag::NONE)
return t_list(tag::NONE);
switch (list_type)
{
case tag::BYTE:
{
auto data = std::vector<t_byte>();
data.reserve(length);
for (int i = 0; i < length; i++)
{
data.push_back(in.read_i8());
}
return t_list(std::move(data));
}
case tag::SHORT:
{
auto data = std::vector<t_short>();
data.reserve(length);
for (int i = 0; i < length; i++)
{
data.push_back(in.read_i16());
}
return t_list(std::move(data));
}
case tag::INT:
{
auto data = std::vector<t_int>();
data.reserve(length);
for (int i = 0; i < length; i++)
{
data.push_back(in.read_i32());
}
return t_list(std::move(data));
}
case tag::LONG:
{
auto data = std::vector<t_long>();
data.reserve(length);
for (int i = 0; i < length; i++)
{
data.push_back(in.read_i64());
}
return t_list(std::move(data));
}
case tag::FLOAT:
{
auto data = std::vector<t_float>();
data.reserve(length);
for (int i = 0; i < length; i++)
{
data.push_back(in.read_f32());
}
return t_list(std::move(data));
}
case tag::DOUBLE:
{
auto data = std::vector<t_double>();
data.reserve(length);
for (int i = 0; i < length; i++)
{
data.push_back(in.read_f64());
}
return t_list(std::move(data));
}
case tag::BYTEARRAY:
{
auto data = std::vector<t_bytearray>();
data.reserve(length);
for (int i = 0; i < length; i++)
{
data.push_back(read_bytearray(in));
}
return t_list(std::move(data));
}
case tag::STRING:
{
auto data = std::vector<t_string>();
data.reserve(length);
for (int i = 0; i < length; i++)
{
data.push_back(in.read_str());
}
return t_list(std::move(data));
}
case tag::LIST:
{
auto data = std::vector<t_list>();
data.reserve(length);
for (int i = 0; i < length; i++)
{
data.push_back(read_list(in));
}
return t_list(std::move(data));
}
case tag::COMPOUND:
{
auto data = std::vector<t_compound>();
data.reserve(length);
for (int i = 0; i < length; i++)
{
data.push_back(read_compound(in));
}
return t_list(std::move(data));
}
case tag::INTARRAY:
{
auto data = std::vector<t_intarray>();
data.reserve(length);
for (int i = 0; i < length; i++)
{
data.push_back(read_intarray(in));
}
return t_list(std::move(data));
}
case tag::LONGARRAY:
{
auto data = std::vector<t_longarray>();
data.reserve(length);
for (int i = 0; i < length; i++)
{
data.push_back(read_longarray(in));
}
return t_list(std::move(data));
}
default:
return t_list(tag::NONE);
}
}
[[nodiscard]] inline t_compound read_compound(nbtin& in)
{
std::vector<std::pair<std::string, t_variant>> map;
tag intype = in.read_type();
while (intype != tag::NONE)
{
std::string key = in.read_str();
t_variant obj = read_tag(in, intype);
map.push_back(std::make_pair(std::move(key), std::move(obj)));
intype = in.read_type();
}
return t_compound(std::move(map));
}
[[nodiscard]] inline t_variant read_tag(nbtin& in, tag type)
{
switch (type)
{
case tag::BYTE:
return in.read_i8();
case tag::SHORT:
return in.read_i16();
case tag::INT:
return in.read_i32();
case tag::LONG:
return in.read_i64();
case tag::FLOAT:
return in.read_f32();
case tag::DOUBLE:
return in.read_f64();
case tag::STRING:
return in.read_str();
case tag::BYTEARRAY:
return read_bytearray(in);
case tag::LIST:
return read_list(in);
case tag::COMPOUND:
return read_compound(in);
case tag::INTARRAY:
return read_intarray(in);
case tag::LONGARRAY:
return read_longarray(in);
}
return nullptr;
}
void write_tag(t_byte value, nbtout& out);
void write_tag(t_short value, nbtout& out);
void write_tag(t_int value, nbtout& out);
void write_tag(t_long value, nbtout& out);
void write_tag(t_float value, nbtout& out);
void write_tag(t_double value, nbtout& out);
void write_tag(const t_bytearray& value, nbtout& out);
void write_tag(const t_string& value, nbtout& out);
void write_tag(const t_list& value, nbtout& out);
void write_tag(const t_compound& value, nbtout& out);
void write_tag(const t_intarray& value, nbtout& out);
void write_tag(const t_longarray& value, nbtout& out);
void write_tag(const t_variant& value, nbtout& out);
inline void write_tag(t_byte value, nbtout& out)
{
out.write(value);
}
inline void write_tag(t_short value, nbtout& out)
{
out.write(value);
}
inline void write_tag(t_int value, nbtout& out)
{
out.write(value);
}
inline void write_tag(t_long value, nbtout& out)
{
out.write(value);
}
inline void write_tag(t_float value, nbtout& out)
{
out.write(value);
}
inline void write_tag(t_double value, nbtout& out)
{
out.write(value);
}
inline void write_tag(const t_string& value, nbtout& out)
{
out.write(value);
}
inline void write_tag(const t_bytearray& value, nbtout& out)
{
out.write(static_cast<int>(value.size()));
for (auto v : value)
{
out.write(v);
}
}
inline void write_tag(const t_intarray& value, nbtout& out)
{
out.write(static_cast<int>(value.size()));
for (auto v : value)
{
out.write(v);
}
}
inline void write_tag(const t_longarray& value, nbtout& out)
{
out.write(static_cast<int>(value.size()));
for (auto v : value)
{
out.write(v);
}
}
inline void write_tag(const t_list& value, nbtout& out)
{
out.write(value.type());
switch (value.type())
{
case tag::BYTE:
{
const std::vector<t_byte>& sub = std::get<std::vector<t_byte>>(value.data);
out.write(static_cast<int>(sub.size()));
for (auto v : sub)
{
out.write(v);
}
}
case tag::SHORT:
{
const std::vector<t_short>& sub = std::get<std::vector<t_short>>(value.data);
out.write(static_cast<int>(sub.size()));
for (auto v : sub)
{
out.write(v);
}
}
case tag::INT:
{
const std::vector<t_int>& sub = std::get<std::vector<t_int>>(value.data);
out.write(static_cast<int>(sub.size()));
for (auto v : sub)
{
out.write(v);
}
}
case tag::LONG:
{
const std::vector<t_long>& sub = std::get<std::vector<t_long>>(value.data);
out.write(static_cast<int>(sub.size()));
for (auto v : sub)
{
out.write(v);
}
}
case tag::FLOAT:
{
const std::vector<t_float>& sub = std::get<std::vector<t_float>>(value.data);
out.write(static_cast<int>(sub.size()));
for (auto v : sub)
{
out.write(v);
}
}
case tag::DOUBLE:
{
const std::vector<t_double>& sub = std::get<std::vector<t_double>>(value.data);
out.write(static_cast<int>(sub.size()));
for (auto v : sub)
{
out.write(v);
}
}
case tag::BYTEARRAY:
{
const std::vector<t_bytearray>& sub = std::get<std::vector<t_bytearray>>(value.data);
out.write(static_cast<int>(sub.size()));
for (auto& v : sub)
{
write_tag(v, out);
}
break;
}
case tag::STRING:
{
const std::vector<t_string>& sub = std::get<std::vector<t_string>>(value.data);
out.write(static_cast<int>(sub.size()));
for (auto& v : sub)
{
out.write(v);
}
break;
}
case tag::LIST:
{
const std::vector<t_list>& sub = std::get<std::vector<t_list>>(value.data);
out.write(static_cast<int>(sub.size()));
for (auto& v : sub)
{
write_tag(v, out);
}
break;
}
case tag::COMPOUND:
{
const std::vector<t_compound>& sub = std::get<std::vector<t_compound>>(value.data);
out.write(static_cast<int>(sub.size()));
for (auto& v : sub)
{
write_tag(v, out);
}
break;
}
case tag::INTARRAY:
{
const std::vector<t_intarray>& sub = std::get<std::vector<t_intarray>>(value.data);
out.write(static_cast<int>(sub.size()));
for (auto& v : sub)
{
write_tag(v, out);
}
break;
}
case tag::LONGARRAY:
{
const std::vector<t_longarray>& sub = std::get<std::vector<t_longarray>>(value.data);
out.write(static_cast<int>(sub.size()));
for (auto& v : sub)
{
write_tag(v, out);
}
break;
}
default:
out.write(int(0));
break;
}
}
inline void write_tag(const t_compound& value, nbtout& out)
{
for (auto& v : value.data)
{
tag v_type = tag(v.second.index());
out.write(v_type);
out.write(v.first);
write_tag(v.second, out);
}
out.write(tag::NONE);
}
inline void write_tag(const t_variant& node, nbtout& out)
{
switch (tag(node.index()))
{
case tag::BYTE:
out.write(std::get<t_byte>(node));
break;
case tag::SHORT:
out.write(std::get<t_short>(node));
break;
case tag::INT:
out.write(std::get<t_int>(node));
break;
case tag::LONG:
out.write(std::get<t_long>(node));
break;
case tag::FLOAT:
out.write(std::get<t_float>(node));
break;
case tag::DOUBLE:
out.write(std::get<t_double>(node));
break;
case tag::BYTEARRAY:
write_tag(std::get<t_bytearray>(node), out);
break;
case tag::STRING:
out.write(std::get<t_string>(node));
break;
case tag::LIST:
write_tag(std::get<t_list>(node), out);
break;
case tag::COMPOUND:
write_tag(std::get<t_compound>(node), out);
break;
case tag::INTARRAY:
write_tag(std::get<t_intarray>(node), out);
break;
case tag::LONGARRAY:
write_tag(std::get<t_longarray>(node), out);
break;
}
}
[[nodiscard]] inline NBTree load(nbtin& in)
{
tag node_type = in.read_type();
std::string node_name = in.read_str();
t_variant node = read_tag(in, node_type);
return NBTree(std::move(node), std::move(node_name));
}
inline void dump(const NBTree& tree, nbtout& out)
{
tag node_type = tag(tree.root.index());
out.write(node_type);
out.write(tree.name);
write_tag(tree.root, out);
}
}
#endif // NBT_TAGS_HEADER_FILE
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment