diff options
| author | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 19:56:58 +0300 |
|---|---|---|
| committer | Mehmet Samet Duman <yongdohyun@projecttick.org> | 2026-04-02 19:56:58 +0300 |
| commit | a4b5ffbaadb591066e2a97f8d450fb1d93e56a6e (patch) | |
| tree | ae7c5841f264eea66484f9a413111ce012fa7a86 /libnbtplusplus/src | |
| parent | 7fb132859fda54aa96bc9dd46d302b343eeb5a02 (diff) | |
| parent | 1a0ffe372f4da8408c5d08a36013536a3396b9e6 (diff) | |
| download | Project-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.cpp | 300 | ||||
| -rw-r--r-- | libnbtplusplus/src/io/izlibstream.cpp | 101 | ||||
| -rw-r--r-- | libnbtplusplus/src/io/ozlibstream.cpp | 114 | ||||
| -rw-r--r-- | libnbtplusplus/src/io/stream_reader.cpp | 122 | ||||
| -rw-r--r-- | libnbtplusplus/src/io/stream_writer.cpp | 59 | ||||
| -rw-r--r-- | libnbtplusplus/src/tag.cpp | 201 | ||||
| -rw-r--r-- | libnbtplusplus/src/tag_compound.cpp | 109 | ||||
| -rw-r--r-- | libnbtplusplus/src/tag_list.cpp | 187 | ||||
| -rw-r--r-- | libnbtplusplus/src/tag_string.cpp | 45 | ||||
| -rw-r--r-- | libnbtplusplus/src/text/json_formatter.cpp | 256 | ||||
| -rw-r--r-- | libnbtplusplus/src/value.cpp | 327 | ||||
| -rw-r--r-- | libnbtplusplus/src/value_initializer.cpp | 49 |
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 |
