summaryrefslogtreecommitdiff
path: root/libnbtplusplus/src
diff options
context:
space:
mode:
authorMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-02 19:56:58 +0300
committerMehmet Samet Duman <yongdohyun@projecttick.org>2026-04-02 19:56:58 +0300
commita4b5ffbaadb591066e2a97f8d450fb1d93e56a6e (patch)
treeae7c5841f264eea66484f9a413111ce012fa7a86 /libnbtplusplus/src
parent7fb132859fda54aa96bc9dd46d302b343eeb5a02 (diff)
parent1a0ffe372f4da8408c5d08a36013536a3396b9e6 (diff)
downloadProject-Tick-a4b5ffbaadb591066e2a97f8d450fb1d93e56a6e.tar.gz
Project-Tick-a4b5ffbaadb591066e2a97f8d450fb1d93e56a6e.zip
Add 'libnbtplusplus/' from commit '1a0ffe372f4da8408c5d08a36013536a3396b9e6'
git-subtree-dir: libnbtplusplus git-subtree-mainline: 7fb132859fda54aa96bc9dd46d302b343eeb5a02 git-subtree-split: 1a0ffe372f4da8408c5d08a36013536a3396b9e6
Diffstat (limited to 'libnbtplusplus/src')
-rw-r--r--libnbtplusplus/src/endian_str.cpp300
-rw-r--r--libnbtplusplus/src/io/izlibstream.cpp101
-rw-r--r--libnbtplusplus/src/io/ozlibstream.cpp114
-rw-r--r--libnbtplusplus/src/io/stream_reader.cpp122
-rw-r--r--libnbtplusplus/src/io/stream_writer.cpp59
-rw-r--r--libnbtplusplus/src/tag.cpp201
-rw-r--r--libnbtplusplus/src/tag_compound.cpp109
-rw-r--r--libnbtplusplus/src/tag_list.cpp187
-rw-r--r--libnbtplusplus/src/tag_string.cpp45
-rw-r--r--libnbtplusplus/src/text/json_formatter.cpp256
-rw-r--r--libnbtplusplus/src/value.cpp327
-rw-r--r--libnbtplusplus/src/value_initializer.cpp49
12 files changed, 1870 insertions, 0 deletions
diff --git a/libnbtplusplus/src/endian_str.cpp b/libnbtplusplus/src/endian_str.cpp
new file mode 100644
index 0000000000..e9553cd9d3
--- /dev/null
+++ b/libnbtplusplus/src/endian_str.cpp
@@ -0,0 +1,300 @@
+// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag <ljfa-ag@web.de>
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+/*
+ * libnbt++ - A library for the Minecraft Named Binary Tag format.
+ * Copyright (C) 2013, 2015 ljfa-ag
+ *
+ * This file is part of libnbt++.
+ *
+ * libnbt++ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libnbt++ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "endian_str.h"
+#include <climits>
+#include <cstring>
+#include <iostream>
+
+static_assert(CHAR_BIT == 8, "Assuming that a byte has 8 bits");
+static_assert(sizeof(float) == 4, "Assuming that a float is 4 byte long");
+static_assert(sizeof(double) == 8, "Assuming that a double is 8 byte long");
+
+namespace endian
+{
+
+ namespace // anonymous
+ {
+ void pun_int_to_float(float& f, uint32_t i)
+ {
+ // Yes we need to do it this way to avoid undefined behavior
+ memcpy(&f, &i, 4);
+ }
+
+ uint32_t pun_float_to_int(float f)
+ {
+ uint32_t ret;
+ memcpy(&ret, &f, 4);
+ return ret;
+ }
+
+ void pun_int_to_double(double& d, uint64_t i)
+ {
+ memcpy(&d, &i, 8);
+ }
+
+ uint64_t pun_double_to_int(double f)
+ {
+ uint64_t ret;
+ memcpy(&ret, &f, 8);
+ return ret;
+ }
+ } // namespace
+
+ //------------------------------------------------------------------------------
+
+ void read_little(std::istream& is, uint8_t& x)
+ {
+ is.get(reinterpret_cast<char&>(x));
+ }
+
+ void read_little(std::istream& is, uint16_t& x)
+ {
+ uint8_t tmp[2];
+ is.read(reinterpret_cast<char*>(tmp), 2);
+ x = uint16_t(tmp[0]) | (uint16_t(tmp[1]) << 8);
+ }
+
+ void read_little(std::istream& is, uint32_t& x)
+ {
+ uint8_t tmp[4];
+ is.read(reinterpret_cast<char*>(tmp), 4);
+ x = uint32_t(tmp[0]) | (uint32_t(tmp[1]) << 8) |
+ (uint32_t(tmp[2]) << 16) | (uint32_t(tmp[3]) << 24);
+ }
+
+ void read_little(std::istream& is, uint64_t& x)
+ {
+ uint8_t tmp[8];
+ is.read(reinterpret_cast<char*>(tmp), 8);
+ x = uint64_t(tmp[0]) | (uint64_t(tmp[1]) << 8) |
+ (uint64_t(tmp[2]) << 16) | (uint64_t(tmp[3]) << 24) |
+ (uint64_t(tmp[4]) << 32) | (uint64_t(tmp[5]) << 40) |
+ (uint64_t(tmp[6]) << 48) | (uint64_t(tmp[7]) << 56);
+ }
+
+ void read_little(std::istream& is, int8_t& x)
+ {
+ read_little(is, reinterpret_cast<uint8_t&>(x));
+ }
+ void read_little(std::istream& is, int16_t& x)
+ {
+ read_little(is, reinterpret_cast<uint16_t&>(x));
+ }
+ void read_little(std::istream& is, int32_t& x)
+ {
+ read_little(is, reinterpret_cast<uint32_t&>(x));
+ }
+ void read_little(std::istream& is, int64_t& x)
+ {
+ read_little(is, reinterpret_cast<uint64_t&>(x));
+ }
+
+ void read_little(std::istream& is, float& x)
+ {
+ uint32_t tmp;
+ read_little(is, tmp);
+ pun_int_to_float(x, tmp);
+ }
+
+ void read_little(std::istream& is, double& x)
+ {
+ uint64_t tmp;
+ read_little(is, tmp);
+ pun_int_to_double(x, tmp);
+ }
+
+ //------------------------------------------------------------------------------
+
+ void read_big(std::istream& is, uint8_t& x)
+ {
+ is.read(reinterpret_cast<char*>(&x), 1);
+ }
+
+ void read_big(std::istream& is, uint16_t& x)
+ {
+ uint8_t tmp[2];
+ is.read(reinterpret_cast<char*>(tmp), 2);
+ x = uint16_t(tmp[1]) | (uint16_t(tmp[0]) << 8);
+ }
+
+ void read_big(std::istream& is, uint32_t& x)
+ {
+ uint8_t tmp[4];
+ is.read(reinterpret_cast<char*>(tmp), 4);
+ x = uint32_t(tmp[3]) | (uint32_t(tmp[2]) << 8) |
+ (uint32_t(tmp[1]) << 16) | (uint32_t(tmp[0]) << 24);
+ }
+
+ void read_big(std::istream& is, uint64_t& x)
+ {
+ uint8_t tmp[8];
+ is.read(reinterpret_cast<char*>(tmp), 8);
+ x = uint64_t(tmp[7]) | (uint64_t(tmp[6]) << 8) |
+ (uint64_t(tmp[5]) << 16) | (uint64_t(tmp[4]) << 24) |
+ (uint64_t(tmp[3]) << 32) | (uint64_t(tmp[2]) << 40) |
+ (uint64_t(tmp[1]) << 48) | (uint64_t(tmp[0]) << 56);
+ }
+
+ void read_big(std::istream& is, int8_t& x)
+ {
+ read_big(is, reinterpret_cast<uint8_t&>(x));
+ }
+ void read_big(std::istream& is, int16_t& x)
+ {
+ read_big(is, reinterpret_cast<uint16_t&>(x));
+ }
+ void read_big(std::istream& is, int32_t& x)
+ {
+ read_big(is, reinterpret_cast<uint32_t&>(x));
+ }
+ void read_big(std::istream& is, int64_t& x)
+ {
+ read_big(is, reinterpret_cast<uint64_t&>(x));
+ }
+
+ void read_big(std::istream& is, float& x)
+ {
+ uint32_t tmp;
+ read_big(is, tmp);
+ pun_int_to_float(x, tmp);
+ }
+
+ void read_big(std::istream& is, double& x)
+ {
+ uint64_t tmp;
+ read_big(is, tmp);
+ pun_int_to_double(x, tmp);
+ }
+
+ //------------------------------------------------------------------------------
+
+ void write_little(std::ostream& os, uint8_t x)
+ {
+ os.put(x);
+ }
+
+ void write_little(std::ostream& os, uint16_t x)
+ {
+ uint8_t tmp[2]{uint8_t(x), uint8_t(x >> 8)};
+ os.write(reinterpret_cast<const char*>(tmp), 2);
+ }
+
+ void write_little(std::ostream& os, uint32_t x)
+ {
+ uint8_t tmp[4]{uint8_t(x), uint8_t(x >> 8), uint8_t(x >> 16),
+ uint8_t(x >> 24)};
+ os.write(reinterpret_cast<const char*>(tmp), 4);
+ }
+
+ void write_little(std::ostream& os, uint64_t x)
+ {
+ uint8_t tmp[8]{uint8_t(x), uint8_t(x >> 8), uint8_t(x >> 16),
+ uint8_t(x >> 24), uint8_t(x >> 32), uint8_t(x >> 40),
+ uint8_t(x >> 48), uint8_t(x >> 56)};
+ os.write(reinterpret_cast<const char*>(tmp), 8);
+ }
+
+ void write_little(std::ostream& os, int8_t x)
+ {
+ write_little(os, static_cast<uint8_t>(x));
+ }
+ void write_little(std::ostream& os, int16_t x)
+ {
+ write_little(os, static_cast<uint16_t>(x));
+ }
+ void write_little(std::ostream& os, int32_t x)
+ {
+ write_little(os, static_cast<uint32_t>(x));
+ }
+ void write_little(std::ostream& os, int64_t x)
+ {
+ write_little(os, static_cast<uint64_t>(x));
+ }
+
+ void write_little(std::ostream& os, float x)
+ {
+ write_little(os, pun_float_to_int(x));
+ }
+
+ void write_little(std::ostream& os, double x)
+ {
+ write_little(os, pun_double_to_int(x));
+ }
+
+ //------------------------------------------------------------------------------
+
+ void write_big(std::ostream& os, uint8_t x)
+ {
+ os.put(x);
+ }
+
+ void write_big(std::ostream& os, uint16_t x)
+ {
+ uint8_t tmp[2]{uint8_t(x >> 8), uint8_t(x)};
+ os.write(reinterpret_cast<const char*>(tmp), 2);
+ }
+
+ void write_big(std::ostream& os, uint32_t x)
+ {
+ uint8_t tmp[4]{uint8_t(x >> 24), uint8_t(x >> 16), uint8_t(x >> 8),
+ uint8_t(x)};
+ os.write(reinterpret_cast<const char*>(tmp), 4);
+ }
+
+ void write_big(std::ostream& os, uint64_t x)
+ {
+ uint8_t tmp[8]{uint8_t(x >> 56), uint8_t(x >> 48), uint8_t(x >> 40),
+ uint8_t(x >> 32), uint8_t(x >> 24), uint8_t(x >> 16),
+ uint8_t(x >> 8), uint8_t(x)};
+ os.write(reinterpret_cast<const char*>(tmp), 8);
+ }
+
+ void write_big(std::ostream& os, int8_t x)
+ {
+ write_big(os, static_cast<uint8_t>(x));
+ }
+ void write_big(std::ostream& os, int16_t x)
+ {
+ write_big(os, static_cast<uint16_t>(x));
+ }
+ void write_big(std::ostream& os, int32_t x)
+ {
+ write_big(os, static_cast<uint32_t>(x));
+ }
+ void write_big(std::ostream& os, int64_t x)
+ {
+ write_big(os, static_cast<uint64_t>(x));
+ }
+
+ void write_big(std::ostream& os, float x)
+ {
+ write_big(os, pun_float_to_int(x));
+ }
+
+ void write_big(std::ostream& os, double x)
+ {
+ write_big(os, pun_double_to_int(x));
+ }
+
+} // namespace endian
diff --git a/libnbtplusplus/src/io/izlibstream.cpp b/libnbtplusplus/src/io/izlibstream.cpp
new file mode 100644
index 0000000000..9d57545360
--- /dev/null
+++ b/libnbtplusplus/src/io/izlibstream.cpp
@@ -0,0 +1,101 @@
+// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag <ljfa-ag@web.de>
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+/*
+ * libnbt++ - A library for the Minecraft Named Binary Tag format.
+ * Copyright (C) 2013, 2015 ljfa-ag
+ *
+ * This file is part of libnbt++.
+ *
+ * libnbt++ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libnbt++ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "io/izlibstream.h"
+#include "io/zlib_streambuf.h"
+
+namespace zlib
+{
+
+ inflate_streambuf::inflate_streambuf(std::istream& input, size_t bufsize,
+ int window_bits)
+ : zlib_streambuf(bufsize), is(input), stream_end(false)
+ {
+ zstr.next_in = Z_NULL;
+ zstr.avail_in = 0;
+ int ret = inflateInit2(&zstr, window_bits);
+ if (ret != Z_OK)
+ throw zlib_error(zstr.msg, ret);
+
+ char* end = out.data() + out.size();
+ setg(end, end, end);
+ }
+
+ inflate_streambuf::~inflate_streambuf() noexcept
+ {
+ inflateEnd(&zstr);
+ }
+
+ inflate_streambuf::int_type inflate_streambuf::underflow()
+ {
+ if (gptr() < egptr())
+ return traits_type::to_int_type(*gptr());
+
+ size_t have;
+ do {
+ // Read if input buffer is empty
+ if (zstr.avail_in <= 0) {
+ is.read(in.data(), in.size());
+ if (is.bad())
+ throw std::ios_base::failure("Input stream is bad");
+ size_t count = is.gcount();
+ if (count == 0 && !stream_end)
+ throw zlib_error("Unexpected end of stream", Z_DATA_ERROR);
+
+ zstr.next_in = reinterpret_cast<Bytef*>(in.data());
+ zstr.avail_in = count;
+ }
+
+ zstr.next_out = reinterpret_cast<Bytef*>(out.data());
+ zstr.avail_out = out.size();
+
+ int ret = inflate(&zstr, Z_NO_FLUSH);
+ have = out.size() - zstr.avail_out;
+ switch (ret) {
+ case Z_NEED_DICT:
+ case Z_DATA_ERROR:
+ throw zlib_error(zstr.msg, ret);
+
+ case Z_MEM_ERROR:
+ throw std::bad_alloc();
+
+ case Z_STREAM_END:
+ if (!stream_end) {
+ stream_end = true;
+ // In case we consumed too much, we have to rewind the
+ // input stream
+ is.clear();
+ is.seekg(-static_cast<std::streamoff>(zstr.avail_in),
+ std::ios_base::cur);
+ }
+ if (have == 0)
+ return traits_type::eof();
+ break;
+ }
+ } while (have == 0);
+
+ setg(out.data(), out.data(), out.data() + have);
+ return traits_type::to_int_type(*gptr());
+ }
+
+} // namespace zlib
diff --git a/libnbtplusplus/src/io/ozlibstream.cpp b/libnbtplusplus/src/io/ozlibstream.cpp
new file mode 100644
index 0000000000..c8a3e862a1
--- /dev/null
+++ b/libnbtplusplus/src/io/ozlibstream.cpp
@@ -0,0 +1,114 @@
+// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag <ljfa-ag@web.de>
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+/*
+ * libnbt++ - A library for the Minecraft Named Binary Tag format.
+ * Copyright (C) 2013, 2015 ljfa-ag
+ *
+ * This file is part of libnbt++.
+ *
+ * libnbt++ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libnbt++ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "io/ozlibstream.h"
+#include "io/zlib_streambuf.h"
+
+namespace zlib
+{
+
+ deflate_streambuf::deflate_streambuf(std::ostream& output, size_t bufsize,
+ int level, int window_bits,
+ int mem_level, int strategy)
+ : zlib_streambuf(bufsize), os(output)
+ {
+ int ret = deflateInit2(&zstr, level, Z_DEFLATED, window_bits, mem_level,
+ strategy);
+ if (ret != Z_OK)
+ throw zlib_error(zstr.msg, ret);
+
+ setp(in.data(), in.data() + in.size());
+ }
+
+ deflate_streambuf::~deflate_streambuf() noexcept
+ {
+ try {
+ close();
+ } catch (...) {
+ // ignore as we can't do anything about it
+ }
+ deflateEnd(&zstr);
+ }
+
+ void deflate_streambuf::close()
+ {
+ deflate_chunk(Z_FINISH);
+ }
+
+ void deflate_streambuf::deflate_chunk(int flush)
+ {
+ zstr.next_in = reinterpret_cast<Bytef*>(pbase());
+ zstr.avail_in = pptr() - pbase();
+ do {
+ zstr.next_out = reinterpret_cast<Bytef*>(out.data());
+ zstr.avail_out = out.size();
+ int ret = deflate(&zstr, flush);
+ if (ret != Z_OK && ret != Z_STREAM_END) {
+ os.setstate(std::ios_base::failbit);
+ throw zlib_error(zstr.msg, ret);
+ }
+ int have = out.size() - zstr.avail_out;
+ if (!os.write(out.data(), have))
+ throw std::ios_base::failure(
+ "Could not write to the output stream");
+ } while (zstr.avail_out == 0);
+ setp(in.data(), in.data() + in.size());
+ }
+
+ deflate_streambuf::int_type deflate_streambuf::overflow(int_type ch)
+ {
+ deflate_chunk();
+ if (ch != traits_type::eof()) {
+ *pptr() = ch;
+ pbump(1);
+ }
+ return ch;
+ }
+
+ int deflate_streambuf::sync()
+ {
+ deflate_chunk();
+ return 0;
+ }
+
+ void ozlibstream::close()
+ {
+ // Capture the exception mask so we can restore it if close() fails.
+ std::ios_base::iostate old_ex = exceptions();
+ try {
+ buf.close();
+ return;
+ } catch (...) {
+ // fall through to mark the stream as bad
+ }
+
+ // Setting the stream state while exceptions are enabled may cause
+ // `setstate` to throw an `ios_base::failure` which would replace
+ // the original exception. Temporarily disable exceptions on this
+ // stream, set the `badbit`, then restore the exception mask.
+ exceptions(std::ios_base::goodbit);
+ setstate(std::ios_base::badbit);
+ exceptions(old_ex);
+ }
+
+} // namespace zlib
diff --git a/libnbtplusplus/src/io/stream_reader.cpp b/libnbtplusplus/src/io/stream_reader.cpp
new file mode 100644
index 0000000000..43032b1120
--- /dev/null
+++ b/libnbtplusplus/src/io/stream_reader.cpp
@@ -0,0 +1,122 @@
+// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag <ljfa-ag@web.de>
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+/*
+ * libnbt++ - A library for the Minecraft Named Binary Tag format.
+ * Copyright (C) 2013, 2015 ljfa-ag
+ *
+ * This file is part of libnbt++.
+ *
+ * libnbt++ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libnbt++ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "io/stream_reader.h"
+#include "make_unique.h"
+#include "tag_compound.h"
+#include <istream>
+
+namespace nbt
+{
+ namespace io
+ {
+
+ static constexpr int MAX_DEPTH = 1024;
+
+ std::pair<std::string, std::unique_ptr<tag_compound>>
+ read_compound(std::istream& is, endian::endian e)
+ {
+ return stream_reader(is, e).read_compound();
+ }
+
+ std::pair<std::string, std::unique_ptr<tag>> read_tag(std::istream& is,
+ endian::endian e)
+ {
+ return stream_reader(is, e).read_tag();
+ }
+
+ stream_reader::stream_reader(std::istream& is,
+ endian::endian e) noexcept
+ : is(is), endian(e)
+ {
+ }
+
+ std::istream& stream_reader::get_istr() const
+ {
+ return is;
+ }
+
+ endian::endian stream_reader::get_endian() const
+ {
+ return endian;
+ }
+
+ std::pair<std::string, std::unique_ptr<tag_compound>>
+ stream_reader::read_compound()
+ {
+ if (read_type() != tag_type::Compound) {
+ is.setstate(std::ios::failbit);
+ throw input_error("Tag is not a compound");
+ }
+ std::string key = read_string();
+ auto comp = make_unique<tag_compound>();
+ comp->read_payload(*this);
+ return {std::move(key), std::move(comp)};
+ }
+
+ std::pair<std::string, std::unique_ptr<tag>> stream_reader::read_tag()
+ {
+ tag_type type = read_type();
+ std::string key = read_string();
+ std::unique_ptr<tag> t = read_payload(type);
+ return {std::move(key), std::move(t)};
+ }
+
+ std::unique_ptr<tag> stream_reader::read_payload(tag_type type)
+ {
+ if (++depth > MAX_DEPTH)
+ throw input_error("Too deeply nested");
+ std::unique_ptr<tag> t = tag::create(type);
+ t->read_payload(*this);
+ --depth;
+ return t;
+ }
+
+ tag_type stream_reader::read_type(bool allow_end)
+ {
+ int type = is.get();
+ if (!is)
+ throw input_error("Error reading tag type");
+ if (!is_valid_type(type, allow_end)) {
+ is.setstate(std::ios::failbit);
+ throw input_error("Invalid tag type: " + std::to_string(type));
+ }
+ return static_cast<tag_type>(type);
+ }
+
+ std::string stream_reader::read_string()
+ {
+ uint16_t len;
+ read_num(len);
+ if (!is)
+ throw input_error("Error reading string");
+
+ std::string ret(len, '\0');
+ is.read(&ret[0], len); // C++11 allows us to do this
+ if (!is)
+ throw input_error("Error reading string");
+ return ret;
+ }
+
+ } // namespace io
+} // namespace nbt
diff --git a/libnbtplusplus/src/io/stream_writer.cpp b/libnbtplusplus/src/io/stream_writer.cpp
new file mode 100644
index 0000000000..5275c5a1b4
--- /dev/null
+++ b/libnbtplusplus/src/io/stream_writer.cpp
@@ -0,0 +1,59 @@
+// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag <ljfa-ag@web.de>
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+/*
+ * libnbt++ - A library for the Minecraft Named Binary Tag format.
+ * Copyright (C) 2013, 2015 ljfa-ag
+ *
+ * This file is part of libnbt++.
+ *
+ * libnbt++ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libnbt++ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "io/stream_writer.h"
+#include <sstream>
+
+namespace nbt
+{
+ namespace io
+ {
+
+ void write_tag(const std::string& key, const tag& t, std::ostream& os,
+ endian::endian e)
+ {
+ stream_writer(os, e).write_tag(key, t);
+ }
+
+ void stream_writer::write_tag(const std::string& key, const tag& t)
+ {
+ write_type(t.get_type());
+ write_string(key);
+ write_payload(t);
+ }
+
+ void stream_writer::write_string(const std::string& str)
+ {
+ if (str.size() > max_string_len) {
+ os.setstate(std::ios::failbit);
+ std::ostringstream sstr;
+ sstr << "String is too long for NBT (" << str.size() << " > "
+ << max_string_len << ")";
+ throw std::length_error(sstr.str());
+ }
+ write_num(static_cast<uint16_t>(str.size()));
+ os.write(str.data(), str.size());
+ }
+
+ } // namespace io
+} // namespace nbt
diff --git a/libnbtplusplus/src/tag.cpp b/libnbtplusplus/src/tag.cpp
new file mode 100644
index 0000000000..8e57548f2d
--- /dev/null
+++ b/libnbtplusplus/src/tag.cpp
@@ -0,0 +1,201 @@
+// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag <ljfa-ag@web.de>
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+/*
+ * libnbt++ - A library for the Minecraft Named Binary Tag format.
+ * Copyright (C) 2013, 2015 ljfa-ag
+ *
+ * This file is part of libnbt++.
+ *
+ * libnbt++ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libnbt++ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "tag.h"
+#include "nbt_tags.h"
+#include "text/json_formatter.h"
+#include <limits>
+#include <ostream>
+#include <stdexcept>
+#include <typeinfo>
+
+namespace nbt
+{
+
+ // Explicit instantiation definitions for tag_primitive
+ template class tag_primitive<int8_t>;
+ template class tag_primitive<int16_t>;
+ template class tag_primitive<int32_t>;
+ template class tag_primitive<int64_t>;
+ template class tag_primitive<float>;
+ template class tag_primitive<double>;
+
+ static_assert(
+ std::numeric_limits<float>::is_iec559 &&
+ std::numeric_limits<double>::is_iec559,
+ "The floating point values for NBT must conform to IEC 559/IEEE 754");
+
+ bool is_valid_type(int type, bool allow_end)
+ {
+ return (allow_end ? 0 : 1) <= type && type <= 12;
+ }
+
+ std::unique_ptr<tag> tag::clone() &&
+ {
+ return std::move(*this).move_clone();
+ }
+
+ namespace
+ {
+ template <typename T>
+ std::unique_ptr<tag> create_numeric_tag(tag_type type, T val)
+ {
+ switch (type) {
+ case tag_type::Byte:
+ return make_unique<tag_byte>(static_cast<int8_t>(val));
+ case tag_type::Short:
+ return make_unique<tag_short>(static_cast<int16_t>(val));
+ case tag_type::Int:
+ return make_unique<tag_int>(static_cast<int32_t>(val));
+ case tag_type::Long:
+ return make_unique<tag_long>(static_cast<int64_t>(val));
+ case tag_type::Float:
+ return make_unique<tag_float>(static_cast<float>(val));
+ case tag_type::Double:
+ return make_unique<tag_double>(static_cast<double>(val));
+ default:
+ throw std::invalid_argument("Invalid numeric tag type");
+ }
+ }
+ } // namespace
+
+ std::unique_ptr<tag> tag::create(tag_type type)
+ {
+ switch (type) {
+ case tag_type::Byte:
+ return make_unique<tag_byte>();
+ case tag_type::Short:
+ return make_unique<tag_short>();
+ case tag_type::Int:
+ return make_unique<tag_int>();
+ case tag_type::Long:
+ return make_unique<tag_long>();
+ case tag_type::Float:
+ return make_unique<tag_float>();
+ case tag_type::Double:
+ return make_unique<tag_double>();
+ case tag_type::Byte_Array:
+ return make_unique<tag_byte_array>();
+ case tag_type::String:
+ return make_unique<tag_string>();
+ case tag_type::List:
+ return make_unique<tag_list>();
+ case tag_type::Compound:
+ return make_unique<tag_compound>();
+ case tag_type::Int_Array:
+ return make_unique<tag_int_array>();
+ case tag_type::Long_Array:
+ return make_unique<tag_long_array>();
+
+ default:
+ throw std::invalid_argument("Invalid tag type");
+ }
+ }
+
+ std::unique_ptr<tag> tag::create(tag_type type, int8_t val)
+ {
+ return create_numeric_tag(type, val);
+ }
+
+ std::unique_ptr<tag> tag::create(tag_type type, int16_t val)
+ {
+ return create_numeric_tag(type, val);
+ }
+
+ std::unique_ptr<tag> tag::create(tag_type type, int32_t val)
+ {
+ return create_numeric_tag(type, val);
+ }
+
+ std::unique_ptr<tag> tag::create(tag_type type, int64_t val)
+ {
+ return create_numeric_tag(type, val);
+ }
+
+ std::unique_ptr<tag> tag::create(tag_type type, float val)
+ {
+ return create_numeric_tag(type, val);
+ }
+
+ std::unique_ptr<tag> tag::create(tag_type type, double val)
+ {
+ return create_numeric_tag(type, val);
+ }
+
+ bool operator==(const tag& lhs, const tag& rhs)
+ {
+ if (typeid(lhs) != typeid(rhs))
+ return false;
+ return lhs.equals(rhs);
+ }
+
+ bool operator!=(const tag& lhs, const tag& rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+ std::ostream& operator<<(std::ostream& os, tag_type tt)
+ {
+ switch (tt) {
+ case tag_type::End:
+ return os << "end";
+ case tag_type::Byte:
+ return os << "byte";
+ case tag_type::Short:
+ return os << "short";
+ case tag_type::Int:
+ return os << "int";
+ case tag_type::Long:
+ return os << "long";
+ case tag_type::Float:
+ return os << "float";
+ case tag_type::Double:
+ return os << "double";
+ case tag_type::Byte_Array:
+ return os << "byte_array";
+ case tag_type::String:
+ return os << "string";
+ case tag_type::List:
+ return os << "list";
+ case tag_type::Compound:
+ return os << "compound";
+ case tag_type::Int_Array:
+ return os << "int_array";
+ case tag_type::Long_Array:
+ return os << "long_array";
+ case tag_type::Null:
+ return os << "null";
+
+ default:
+ return os << "invalid";
+ }
+ }
+
+ std::ostream& operator<<(std::ostream& os, const tag& t)
+ {
+ static const text::json_formatter formatter;
+ formatter.print(os, t);
+ return os;
+ }
+
+} // namespace nbt
diff --git a/libnbtplusplus/src/tag_compound.cpp b/libnbtplusplus/src/tag_compound.cpp
new file mode 100644
index 0000000000..c35ef5d53f
--- /dev/null
+++ b/libnbtplusplus/src/tag_compound.cpp
@@ -0,0 +1,109 @@
+// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag <ljfa-ag@web.de>
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+/*
+ * libnbt++ - A library for the Minecraft Named Binary Tag format.
+ * Copyright (C) 2013, 2015 ljfa-ag
+ *
+ * This file is part of libnbt++.
+ *
+ * libnbt++ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libnbt++ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "tag_compound.h"
+#include "io/stream_reader.h"
+#include "io/stream_writer.h"
+#include <istream>
+#include <sstream>
+
+namespace nbt
+{
+
+ tag_compound::tag_compound(
+ std::initializer_list<std::pair<std::string, value_initializer>> init)
+ {
+ for (const auto& pair : init)
+ tags.emplace(std::move(pair.first), std::move(pair.second));
+ }
+
+ value& tag_compound::at(const std::string& key)
+ {
+ return tags.at(key);
+ }
+
+ const value& tag_compound::at(const std::string& key) const
+ {
+ return tags.at(key);
+ }
+
+ std::pair<tag_compound::iterator, bool>
+ tag_compound::put(const std::string& key, value_initializer&& val)
+ {
+ auto it = tags.find(key);
+ if (it != tags.end()) {
+ it->second = std::move(val);
+ return {it, false};
+ } else {
+ return tags.emplace(key, std::move(val));
+ }
+ }
+
+ std::pair<tag_compound::iterator, bool>
+ tag_compound::insert(const std::string& key, value_initializer&& val)
+ {
+ return tags.emplace(key, std::move(val));
+ }
+
+ bool tag_compound::erase(const std::string& key)
+ {
+ return tags.erase(key) != 0;
+ }
+
+ bool tag_compound::has_key(const std::string& key) const
+ {
+ return tags.find(key) != tags.end();
+ }
+
+ bool tag_compound::has_key(const std::string& key, tag_type type) const
+ {
+ auto it = tags.find(key);
+ return it != tags.end() && it->second.get_type() == type;
+ }
+
+ void tag_compound::read_payload(io::stream_reader& reader)
+ {
+ clear();
+ tag_type tt;
+ while ((tt = reader.read_type(true)) != tag_type::End) {
+ std::string key;
+ try {
+ key = reader.read_string();
+ } catch (io::input_error& ex) {
+ std::ostringstream str;
+ str << "Error reading key of tag_" << tt;
+ throw io::input_error(str.str());
+ }
+ auto tptr = reader.read_payload(tt);
+ tags.emplace(std::move(key), value(std::move(tptr)));
+ }
+ }
+
+ void tag_compound::write_payload(io::stream_writer& writer) const
+ {
+ for (const auto& pair : tags)
+ writer.write_tag(pair.first, pair.second);
+ writer.write_type(tag_type::End);
+ }
+
+} // namespace nbt
diff --git a/libnbtplusplus/src/tag_list.cpp b/libnbtplusplus/src/tag_list.cpp
new file mode 100644
index 0000000000..413af779ae
--- /dev/null
+++ b/libnbtplusplus/src/tag_list.cpp
@@ -0,0 +1,187 @@
+// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag <ljfa-ag@web.de>
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+/*
+ * libnbt++ - A library for the Minecraft Named Binary Tag format.
+ * Copyright (C) 2013, 2015 ljfa-ag
+ *
+ * This file is part of libnbt++.
+ *
+ * libnbt++ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libnbt++ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "tag_list.h"
+#include "nbt_tags.h"
+#include "io/stream_reader.h"
+#include "io/stream_writer.h"
+#include <istream>
+
+namespace nbt
+{
+
+ tag_list::tag_list(std::initializer_list<int8_t> il)
+ {
+ init<tag_byte>(il);
+ }
+ tag_list::tag_list(std::initializer_list<int16_t> il)
+ {
+ init<tag_short>(il);
+ }
+ tag_list::tag_list(std::initializer_list<int32_t> il)
+ {
+ init<tag_int>(il);
+ }
+ tag_list::tag_list(std::initializer_list<int64_t> il)
+ {
+ init<tag_long>(il);
+ }
+ tag_list::tag_list(std::initializer_list<float> il)
+ {
+ init<tag_float>(il);
+ }
+ tag_list::tag_list(std::initializer_list<double> il)
+ {
+ init<tag_double>(il);
+ }
+ tag_list::tag_list(std::initializer_list<std::string> il)
+ {
+ init<tag_string>(il);
+ }
+ tag_list::tag_list(std::initializer_list<tag_byte_array> il)
+ {
+ init<tag_byte_array>(il);
+ }
+ tag_list::tag_list(std::initializer_list<tag_list> il)
+ {
+ init<tag_list>(il);
+ }
+ tag_list::tag_list(std::initializer_list<tag_compound> il)
+ {
+ init<tag_compound>(il);
+ }
+ tag_list::tag_list(std::initializer_list<tag_int_array> il)
+ {
+ init<tag_int_array>(il);
+ }
+ tag_list::tag_list(std::initializer_list<tag_long_array> il)
+ {
+ init<tag_long_array>(il);
+ }
+
+ tag_list::tag_list(std::initializer_list<value> init)
+ {
+ if (init.size() == 0)
+ el_type_ = tag_type::Null;
+ else {
+ el_type_ = init.begin()->get_type();
+ for (const value& val : init) {
+ if (!val || val.get_type() != el_type_)
+ throw std::invalid_argument(
+ "The values are not all the same type");
+ }
+ tags.assign(init.begin(), init.end());
+ }
+ }
+
+ value& tag_list::at(size_t i)
+ {
+ return tags.at(i);
+ }
+
+ const value& tag_list::at(size_t i) const
+ {
+ return tags.at(i);
+ }
+
+ void tag_list::set(size_t i, value&& val)
+ {
+ if (val.get_type() != el_type_)
+ throw std::invalid_argument(
+ "The tag type does not match the list's content type");
+ tags.at(i) = std::move(val);
+ }
+
+ void tag_list::push_back(value_initializer&& val)
+ {
+ if (!val) // don't allow null values
+ throw std::invalid_argument("The value must not be null");
+ if (el_type_ == tag_type::Null) // set content type if undetermined
+ el_type_ = val.get_type();
+ else if (el_type_ != val.get_type())
+ throw std::invalid_argument(
+ "The tag type does not match the list's content type");
+ tags.push_back(std::move(val));
+ }
+
+ void tag_list::reset(tag_type type)
+ {
+ clear();
+ el_type_ = type;
+ }
+
+ void tag_list::read_payload(io::stream_reader& reader)
+ {
+ tag_type lt = reader.read_type(true);
+
+ int32_t length;
+ reader.read_num(length);
+ if (length < 0)
+ reader.get_istr().setstate(std::ios::failbit);
+ if (!reader.get_istr())
+ throw io::input_error("Error reading length of tag_list");
+
+ if (lt != tag_type::End) {
+ reset(lt);
+ tags.reserve(length);
+
+ for (int32_t i = 0; i < length; ++i)
+ tags.emplace_back(reader.read_payload(lt));
+ } else {
+ // In case of tag_end, ignore the length and leave the type
+ // undetermined
+ reset(tag_type::Null);
+ }
+ }
+
+ void tag_list::write_payload(io::stream_writer& writer) const
+ {
+ if (size() > io::stream_writer::max_array_len) {
+ writer.get_ostr().setstate(std::ios::failbit);
+ throw std::length_error("List is too large for NBT");
+ }
+ writer.write_type(el_type_ != tag_type::Null ? el_type_
+ : tag_type::End);
+ writer.write_num(static_cast<int32_t>(size()));
+ for (const auto& val : tags) {
+ // check if the value is of the correct type
+ if (val.get_type() != el_type_) {
+ writer.get_ostr().setstate(std::ios::failbit);
+ throw std::logic_error(
+ "The tags in the list do not all match the content type");
+ }
+ writer.write_payload(val);
+ }
+ }
+
+ bool operator==(const tag_list& lhs, const tag_list& rhs)
+ {
+ return lhs.el_type_ == rhs.el_type_ && lhs.tags == rhs.tags;
+ }
+
+ bool operator!=(const tag_list& lhs, const tag_list& rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+} // namespace nbt
diff --git a/libnbtplusplus/src/tag_string.cpp b/libnbtplusplus/src/tag_string.cpp
new file mode 100644
index 0000000000..481ad19dee
--- /dev/null
+++ b/libnbtplusplus/src/tag_string.cpp
@@ -0,0 +1,45 @@
+// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag <ljfa-ag@web.de>
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+/*
+ * libnbt++ - A library for the Minecraft Named Binary Tag format.
+ * Copyright (C) 2013, 2015 ljfa-ag
+ *
+ * This file is part of libnbt++.
+ *
+ * libnbt++ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libnbt++ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "tag_string.h"
+#include "io/stream_reader.h"
+#include "io/stream_writer.h"
+
+namespace nbt
+{
+
+ void tag_string::read_payload(io::stream_reader& reader)
+ {
+ try {
+ value = reader.read_string();
+ } catch (io::input_error& ex) {
+ throw io::input_error("Error reading tag_string");
+ }
+ }
+
+ void tag_string::write_payload(io::stream_writer& writer) const
+ {
+ writer.write_string(value);
+ }
+
+} // namespace nbt
diff --git a/libnbtplusplus/src/text/json_formatter.cpp b/libnbtplusplus/src/text/json_formatter.cpp
new file mode 100644
index 0000000000..46d726a3c9
--- /dev/null
+++ b/libnbtplusplus/src/text/json_formatter.cpp
@@ -0,0 +1,256 @@
+// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag <ljfa-ag@web.de>
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+/*
+ * libnbt++ - A library for the Minecraft Named Binary Tag format.
+ * Copyright (C) 2013, 2015 ljfa-ag
+ *
+ * This file is part of libnbt++.
+ *
+ * libnbt++ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libnbt++ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "text/json_formatter.h"
+#include "nbt_tags.h"
+#include "nbt_visitor.h"
+#include <cmath>
+#include <iomanip>
+#include <limits>
+
+namespace nbt
+{
+ namespace text
+ {
+
+ namespace // anonymous
+ {
+ /// Helper class which uses the Visitor pattern to pretty-print tags
+ class json_fmt_visitor : public const_nbt_visitor
+ {
+ public:
+ json_fmt_visitor(std::ostream& os) : os(os) {}
+
+ void visit(const tag_byte& b) override
+ {
+ os << static_cast<int>(b.get()) << "b";
+ } // We don't want to print a character
+
+ void visit(const tag_short& s) override
+ {
+ os << s.get() << "s";
+ }
+
+ void visit(const tag_int& i) override
+ {
+ os << i.get();
+ }
+
+ void visit(const tag_long& l) override
+ {
+ os << l.get() << "l";
+ }
+
+ void visit(const tag_float& f) override
+ {
+ write_float(f.get());
+ os << "f";
+ }
+
+ void visit(const tag_double& d) override
+ {
+ write_float(d.get());
+ os << "d";
+ }
+
+ void visit(const tag_byte_array& ba) override
+ {
+ os << "[" << ba.size() << " bytes]";
+ }
+
+ void visit(const tag_string& s) override
+ {
+ os << '"';
+ write_escaped_string(s.get());
+ os << '"';
+ }
+
+ void visit(const tag_list& l) override
+ {
+ // Wrap lines for lists of lists or compounds.
+ // Lists of other types can usually be on one line without
+ // problem.
+ const bool break_lines =
+ l.size() > 0 && (l.el_type() == tag_type::List ||
+ l.el_type() == tag_type::Compound);
+
+ os << "[";
+ if (break_lines) {
+ os << "\n";
+ ++indent_lvl;
+ for (unsigned int i = 0; i < l.size(); ++i) {
+ indent();
+ if (l[i])
+ l[i].get().accept(*this);
+ else
+ write_null();
+ if (i != l.size() - 1)
+ os << ",";
+ os << "\n";
+ }
+ --indent_lvl;
+ indent();
+ } else {
+ for (unsigned int i = 0; i < l.size(); ++i) {
+ if (l[i])
+ l[i].get().accept(*this);
+ else
+ write_null();
+ if (i != l.size() - 1)
+ os << ", ";
+ }
+ }
+ os << "]";
+ }
+
+ void visit(const tag_compound& c) override
+ {
+ if (c.size() ==
+ 0) // No line breaks inside empty compounds please
+ {
+ os << "{}";
+ return;
+ }
+
+ os << "{\n";
+ ++indent_lvl;
+ unsigned int i = 0;
+ for (const auto& kv : c) {
+ indent();
+ os << kv.first << ": ";
+ if (kv.second)
+ kv.second.get().accept(*this);
+ else
+ write_null();
+ if (i != c.size() - 1)
+ os << ",";
+ os << "\n";
+ ++i;
+ }
+ --indent_lvl;
+ indent();
+ os << "}";
+ }
+
+ void visit(const tag_int_array& ia) override
+ {
+ os << "[";
+ for (unsigned int i = 0; i < ia.size(); ++i) {
+ os << ia[i];
+ if (i != ia.size() - 1)
+ os << ", ";
+ }
+ os << "]";
+ }
+
+ void visit(const tag_long_array& la) override
+ {
+ os << "[";
+ for (unsigned int i = 0; i < la.size(); ++i) {
+ os << la[i];
+ if (i != la.size() - 1)
+ os << ", ";
+ }
+ os << "]";
+ }
+
+ private:
+ const std::string indent_str = " ";
+
+ std::ostream& os;
+ int indent_lvl = 0;
+
+ void indent()
+ {
+ for (int i = 0; i < indent_lvl; ++i)
+ os << indent_str;
+ }
+
+ template <class T>
+ void write_float(
+ T val, int precision = std::numeric_limits<T>::max_digits10)
+ {
+ if (std::isfinite(val))
+ os << std::setprecision(precision) << val;
+ else if (std::isinf(val)) {
+ if (std::signbit(val))
+ os << "-";
+ os << "Infinity";
+ } else
+ os << "NaN";
+ }
+
+ void write_null()
+ {
+ os << "null";
+ }
+
+ void write_escaped_string(const std::string& str)
+ {
+ for (char c : str) {
+ switch (c) {
+ case '"':
+ os << "\\\"";
+ break;
+ case '\\':
+ os << "\\\\";
+ break;
+ case '\b':
+ os << "\\b";
+ break;
+ case '\f':
+ os << "\\f";
+ break;
+ case '\n':
+ os << "\\n";
+ break;
+ case '\r':
+ os << "\\r";
+ break;
+ case '\t':
+ os << "\\t";
+ break;
+ default:
+ if (c < 32 || c == 127) {
+ // Control characters, escape as \u00XX
+ os << "\\u00" << std::hex << std::setw(2)
+ << std::setfill('0')
+ << static_cast<int>(c);
+ } else {
+ os << c;
+ }
+ break;
+ }
+ }
+ }
+ };
+ } // namespace
+
+ void json_formatter::print(std::ostream& os, const tag& t) const
+ {
+ json_fmt_visitor v(os);
+ t.accept(v);
+ }
+
+ } // namespace text
+} // namespace nbt
diff --git a/libnbtplusplus/src/value.cpp b/libnbtplusplus/src/value.cpp
new file mode 100644
index 0000000000..3a9ccb71bc
--- /dev/null
+++ b/libnbtplusplus/src/value.cpp
@@ -0,0 +1,327 @@
+// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag <ljfa-ag@web.de>
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+/*
+ * libnbt++ - A library for the Minecraft Named Binary Tag format.
+ * Copyright (C) 2013, 2015 ljfa-ag
+ *
+ * This file is part of libnbt++.
+ *
+ * libnbt++ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libnbt++ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "value.h"
+#include "nbt_tags.h"
+#include <typeinfo>
+#include <algorithm>
+#include <initializer_list>
+#include <cstdint>
+#include <memory>
+
+namespace nbt
+{
+
+ value::value(tag&& t) : tag_(std::move(t).move_clone()) {}
+
+ value::value(const value& rhs)
+ : tag_(rhs.tag_ ? rhs.tag_->clone() : nullptr)
+ {
+ }
+
+ value& value::operator=(const value& rhs)
+ {
+ if (this != &rhs) {
+ tag_ = rhs.tag_ ? rhs.tag_->clone() : nullptr;
+ }
+ return *this;
+ }
+
+ value& value::operator=(tag&& t)
+ {
+ set(std::move(t));
+ return *this;
+ }
+
+ void value::set(tag&& t)
+ {
+ if (tag_)
+ tag_->assign(std::move(t));
+ else
+ tag_ = std::move(t).move_clone();
+ }
+
+ // Primitive assignment
+ namespace // helper functions local to this translation unit
+ {
+ template <typename T>
+ void assign_numeric_impl(std::unique_ptr<tag>& tag_ptr, T val,
+ tag_type default_type)
+ {
+ using nbt::tag_type;
+ if (!tag_ptr) {
+ tag_ptr = tag::create(default_type, val);
+ return;
+ }
+
+ // Determine the incoming tag type for T
+ auto incoming_type = detail::get_primitive_type<T>::value;
+
+ // If the existing tag is of a narrower type than the incoming type,
+ // reject the assignment to avoid widening the stored tag type.
+ auto existing_type = tag_ptr->get_type();
+
+ if (static_cast<int>(existing_type) <
+ static_cast<int>(incoming_type)) {
+ throw std::bad_cast();
+ }
+
+ // Existing type is same or wider: write into the existing tag (may
+ // narrow)
+ switch (existing_type) {
+ case tag_type::Byte:
+ static_cast<tag_byte&>(*tag_ptr).set(
+ static_cast<int8_t>(val));
+ break;
+ case tag_type::Short:
+ static_cast<tag_short&>(*tag_ptr).set(
+ static_cast<int16_t>(val));
+ break;
+ case tag_type::Int:
+ static_cast<tag_int&>(*tag_ptr).set(
+ static_cast<int32_t>(val));
+ break;
+ case tag_type::Long:
+ static_cast<tag_long&>(*tag_ptr).set(
+ static_cast<int64_t>(val));
+ break;
+ case tag_type::Float:
+ static_cast<tag_float&>(*tag_ptr).set(
+ static_cast<float>(val));
+ break;
+ case tag_type::Double:
+ static_cast<tag_double&>(*tag_ptr).set(
+ static_cast<double>(val));
+ break;
+ default:
+ throw std::bad_cast();
+ }
+ }
+ } // namespace
+
+ value& value::operator=(int8_t val)
+ {
+ assign_numeric_impl(tag_, val, tag_type::Byte);
+ return *this;
+ }
+
+ value& value::operator=(int16_t val)
+ {
+ assign_numeric_impl(tag_, val, tag_type::Short);
+ return *this;
+ }
+
+ value& value::operator=(int32_t val)
+ {
+ assign_numeric_impl(tag_, val, tag_type::Int);
+ return *this;
+ }
+
+ value& value::operator=(int64_t val)
+ {
+ assign_numeric_impl(tag_, val, tag_type::Long);
+ return *this;
+ }
+
+ value& value::operator=(float val)
+ {
+ assign_numeric_impl(tag_, val, tag_type::Float);
+ return *this;
+ }
+
+ value& value::operator=(double val)
+ {
+ assign_numeric_impl(tag_, val, tag_type::Double);
+ return *this;
+ }
+
+ // Primitive conversion
+ value::operator int8_t() const
+ {
+ switch (tag_->get_type()) {
+ case tag_type::Byte:
+ return static_cast<tag_byte&>(*tag_).get();
+
+ default:
+ throw std::bad_cast();
+ }
+ }
+
+ value::operator int16_t() const
+ {
+ switch (tag_->get_type()) {
+ case tag_type::Byte:
+ return static_cast<tag_byte&>(*tag_).get();
+ case tag_type::Short:
+ return static_cast<tag_short&>(*tag_).get();
+
+ default:
+ throw std::bad_cast();
+ }
+ }
+
+ value::operator int32_t() const
+ {
+ switch (tag_->get_type()) {
+ case tag_type::Byte:
+ return static_cast<tag_byte&>(*tag_).get();
+ case tag_type::Short:
+ return static_cast<tag_short&>(*tag_).get();
+ case tag_type::Int:
+ return static_cast<tag_int&>(*tag_).get();
+
+ default:
+ throw std::bad_cast();
+ }
+ }
+
+ value::operator int64_t() const
+ {
+ switch (tag_->get_type()) {
+ case tag_type::Byte:
+ return static_cast<tag_byte&>(*tag_).get();
+ case tag_type::Short:
+ return static_cast<tag_short&>(*tag_).get();
+ case tag_type::Int:
+ return static_cast<tag_int&>(*tag_).get();
+ case tag_type::Long:
+ return static_cast<tag_long&>(*tag_).get();
+
+ default:
+ throw std::bad_cast();
+ }
+ }
+
+ value::operator float() const
+ {
+ switch (tag_->get_type()) {
+ case tag_type::Byte:
+ return static_cast<tag_byte&>(*tag_).get();
+ case tag_type::Short:
+ return static_cast<tag_short&>(*tag_).get();
+ case tag_type::Int:
+ return static_cast<tag_int&>(*tag_).get();
+ case tag_type::Long:
+ return static_cast<tag_long&>(*tag_).get();
+ case tag_type::Float:
+ return static_cast<tag_float&>(*tag_).get();
+
+ default:
+ throw std::bad_cast();
+ }
+ }
+
+ value::operator double() const
+ {
+ switch (tag_->get_type()) {
+ case tag_type::Byte:
+ return static_cast<tag_byte&>(*tag_).get();
+ case tag_type::Short:
+ return static_cast<tag_short&>(*tag_).get();
+ case tag_type::Int:
+ return static_cast<tag_int&>(*tag_).get();
+ case tag_type::Long:
+ return static_cast<tag_long&>(*tag_).get();
+ case tag_type::Float:
+ return static_cast<tag_float&>(*tag_).get();
+ case tag_type::Double:
+ return static_cast<tag_double&>(*tag_).get();
+
+ default:
+ throw std::bad_cast();
+ }
+ }
+
+ value& value::operator=(std::string&& str)
+ {
+ if (!tag_)
+ set(tag_string(std::move(str)));
+ else
+ dynamic_cast<tag_string&>(*tag_).set(std::move(str));
+ return *this;
+ }
+
+ value::operator const std::string&() const
+ {
+ return dynamic_cast<tag_string&>(*tag_).get();
+ }
+
+ value& value::at(const std::string& key)
+ {
+ return dynamic_cast<tag_compound&>(*tag_).at(key);
+ }
+
+ const value& value::at(const std::string& key) const
+ {
+ return dynamic_cast<const tag_compound&>(*tag_).at(key);
+ }
+
+ value& value::operator[](const std::string& key)
+ {
+ return dynamic_cast<tag_compound&>(*tag_)[key];
+ }
+
+ value& value::operator[](const char* key)
+ {
+ return (*this)[std::string(key)];
+ }
+
+ value& value::at(size_t i)
+ {
+ return dynamic_cast<tag_list&>(*tag_).at(i);
+ }
+
+ const value& value::at(size_t i) const
+ {
+ return dynamic_cast<const tag_list&>(*tag_).at(i);
+ }
+
+ value& value::operator[](size_t i)
+ {
+ return dynamic_cast<tag_list&>(*tag_)[i];
+ }
+
+ const value& value::operator[](size_t i) const
+ {
+ return dynamic_cast<const tag_list&>(*tag_)[i];
+ }
+
+ tag_type value::get_type() const
+ {
+ return tag_ ? tag_->get_type() : tag_type::Null;
+ }
+
+ bool operator==(const value& lhs, const value& rhs)
+ {
+ if (lhs.tag_ != nullptr && rhs.tag_ != nullptr)
+ return *lhs.tag_ == *rhs.tag_;
+ else
+ return lhs.tag_ == nullptr && rhs.tag_ == nullptr;
+ }
+
+ bool operator!=(const value& lhs, const value& rhs)
+ {
+ return !(lhs == rhs);
+ }
+
+} // namespace nbt
diff --git a/libnbtplusplus/src/value_initializer.cpp b/libnbtplusplus/src/value_initializer.cpp
new file mode 100644
index 0000000000..ec52f544fe
--- /dev/null
+++ b/libnbtplusplus/src/value_initializer.cpp
@@ -0,0 +1,49 @@
+// SPDX-FileCopyrightText: 2013, 2015 ljfa-ag <ljfa-ag@web.de>
+//
+// SPDX-License-Identifier: LGPL-3.0-or-later
+
+/*
+ * libnbt++ - A library for the Minecraft Named Binary Tag format.
+ * Copyright (C) 2013, 2015 ljfa-ag
+ *
+ * This file is part of libnbt++.
+ *
+ * libnbt++ is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * libnbt++ is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with libnbt++. If not, see <http://www.gnu.org/licenses/>.
+ */
+#include "value_initializer.h"
+#include "nbt_tags.h"
+
+namespace nbt
+{
+
+ value_initializer::value_initializer(int8_t val) : value(tag_byte(val)) {}
+ value_initializer::value_initializer(int16_t val) : value(tag_short(val)) {}
+ value_initializer::value_initializer(int32_t val) : value(tag_int(val)) {}
+ value_initializer::value_initializer(int64_t val) : value(tag_long(val)) {}
+ value_initializer::value_initializer(float val) : value(tag_float(val)) {}
+ value_initializer::value_initializer(double val) : value(tag_double(val)) {}
+ value_initializer::value_initializer(const std::string& str)
+ : value(tag_string(str))
+ {
+ }
+ value_initializer::value_initializer(std::string&& str)
+ : value(tag_string(std::move(str)))
+ {
+ }
+ value_initializer::value_initializer(const char* str)
+ : value(tag_string(str))
+ {
+ }
+
+} // namespace nbt