// Copyright 2022, FOSS-VG Developers and Contributers // // This program is free software: you can redistribute it and/or modify it // under the terms of the GNU Affero General Public License as published // by the Free Software Foundation, version 3. // // This program 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 Affero General Public License for more details. // // You should have received a copy of the GNU Affero General Public License // version 3 along with this program. // If not, see https://www.gnu.org/licenses/agpl-3.0.en.html // information taken from https://wiki.vg/NBT // This is an attempt at creating a uniform model for all NBT tags to allow for a uniform interface based on subclassing a single NBT tag super class. // NBT tags have a type, optionally a name which consists of the name size and the name string, optionally content type, and optionally a payload which can consist of optionally content type, optionally a content size, // and the stored content. The format in which they are stored is as follows: . All numbers are stored in big endian representation. // All tag types: // Generic: Tag(uint8:tag_type, String:name, uint16:name_size, byte[]:content, int32:size) // End: Tag( 0, "", 0, None, 0) => used to determine the end of a compound tag, only the type gets stored // Int8: Tag( 1, String:name, uint16:name_size, int8:content, 1) => a single signed byte, size not stored // Int16: Tag( 2, String:name, uint16:name_size, int16:content, 2) => 16 bit signed integer, size not stored // Int32: Tag( 3, String:name, uint16:name_size, int32:content, 4) => 32 bit signed integer, size not stored // Int64: Tag( 4, String:name, uint16:name_size, int64:content, 8) => 64 bit signed integer, size not stored // Float: Tag( 5, String:name, uint16:name_size, float:content, 4) => 32 bit IEEE754 floating point number, size not stored // Double: Tag( 6, String:name, uint16:name_size, double:content, 8) => 64 bit IEEE754 floating point number, size not stored // Int8Array: Tag( 7, String:name, uint16:name_size, int8[]:content, int32:length) => content stored prefixed with size // String: Tag( 8, String:name, uint16:name_size, String:content, uint16:size) => Java style modified UTF-8 string, content stored prefixed with size // List: Tag( 9, String:name, uint16:name_size, Tag[]:content, int32:length) => list of tags of the same type with tag type and name information omitted prefixed by (in order) content type and length // Compound: Tag(10, String:name, uint16:name_size, Tag[]:content, int32:length) => list of tags, last tag is always an end tag, size not stored // Int32Array: Tag(11, String:name, uint16:name_size, int32[]:content,int32:length) => list of 32 bit signed integers prefixed with its size // Int64Array: Tag(12, String:name, uint16:name_size, int64[]:content,int32:length) => list of 64 bit signed integers prefixed with its size #pragma once #include #include #include #include #include "error.hpp" namespace NBT { namespace Helper { ErrorOr readInt8(uint8_t data[], uint64_t dataSize, uint64_t currentPosition); ErrorOr readInt16(uint8_t data[], uint64_t dataSize, uint64_t currentPosition); ErrorOr readInt32(uint8_t data[], uint64_t dataSize, uint64_t currentPosition); ErrorOr readInt64(uint8_t data[], uint64_t dataSize, uint64_t currentPosition); ErrorOr readFloat(uint8_t data[], uint64_t dataSize, uint64_t currentPosition); ErrorOr readDouble(uint8_t data[], uint64_t dataSize, uint64_t currentPosition); ErrorOr> readInt8Array(uint8_t data[], uint64_t dataSize, uint64_t currentPosition); ErrorOr readString(uint8_t data[], uint64_t dataSize, uint64_t currentPosition); ErrorOr> readInt32Array(uint8_t data[], uint64_t dataSize, uint64_t currentPosition); ErrorOr> readInt64Array(uint8_t data[], uint64_t dataSize, uint64_t currentPosition); void writeInt8(std::vector* destination, int8_t data); void writeInt16(std::vector* destination, int16_t data); void writeInt32(std::vector* destination, int32_t data); void writeInt64(std::vector* destination, int64_t data); void writeFloat(std::vector* destination, float data); void writeDouble(std::vector* destination, double data); void writeInt8Array(std::vector* destination, std::vector data); void writeInt8Array(std::vector* destination, int8_t data[], uint32_t dataSize); ErrorOrVoid writeString(std::vector* destination, tiny_utf8::string data); void writeInt32Array(std::vector* destination, std::vector data); void writeInt32Array(std::vector* destination, int32_t data[], uint32_t dataSize); void writeInt64Array(std::vector* destination, std::vector data); void writeInt64Array(std::vector* destination, int64_t data[], uint32_t dataSize); ErrorOr totalTagSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition); ErrorOr containedDataLength(uint8_t data[], uint64_t dataSize, uint64_t currentPosition); } namespace TagType { const uint8_t END = 0; const uint8_t INT8 = 1; const uint8_t INT16 = 2; const uint8_t INT32 = 3; const uint8_t INT64 = 4; const uint8_t FLOAT = 5; const uint8_t DOUBLE = 6; const uint8_t INT8_ARRAY = 7; const uint8_t STRING = 8; const uint8_t LIST = 9; const uint8_t COMPOUND = 10; const uint8_t INT32_ARRAY= 11; const uint8_t INT64_ARRAY= 12; // This is a workaround that's not part of the spec. const uint8_t INVALID = 255; } namespace Tag { class Generic { protected: std::mutex mutex; uint8_t type; public: tiny_utf8::string name; Generic(); virtual ~Generic(); virtual ErrorOrVoid serialize(std::vector* rawData); virtual ErrorOrVoid serializeWithoutHeader(std::vector* rawData); uint8_t getTagType(); }; class End: public Generic { public: End(); // This needs a separate serializer because // END tags have a special header. ErrorOrVoid serialize(std::vector* rawData) override; ErrorOrVoid serializeWithoutHeader(std::vector* rawData) override; }; class Int8: public Generic { private: int8_t value; public: Int8(); Int8(tiny_utf8::string name, int8_t value); ErrorOrVoid serializeWithoutHeader(std::vector* rawData) override; int8_t getValue(); void setValue(int8_t value); }; class Int16: public Generic { private: int16_t value; public: Int16(); Int16(tiny_utf8::string name, int16_t value); ErrorOrVoid serializeWithoutHeader(std::vector* rawData) override; int16_t getValue(); void setValue(int16_t value); }; class Int32: public Generic { private: int32_t value; public: Int32(); Int32(tiny_utf8::string name, int32_t value); ErrorOrVoid serializeWithoutHeader(std::vector* rawData) override; int32_t getValue(); void setValue(int32_t value); }; class Int64: public Generic { private: int64_t value; public: Int64(); Int64(tiny_utf8::string name, int64_t value); ErrorOrVoid serializeWithoutHeader(std::vector* rawData) override; int64_t getValue(); void setValue(int64_t value); }; class Float: public Generic { private: float value; public: Float(); Float(tiny_utf8::string name, float value); ErrorOrVoid serializeWithoutHeader(std::vector* rawData) override; float getValue(); void setValue(float value); }; class Double: public Generic { private: double value; public: Double(); Double(tiny_utf8::string name, double value); ErrorOrVoid serializeWithoutHeader(std::vector* rawData) override; double getValue(); void setValue(double value); }; class Int8Array: public Generic { private: std::vector data; public: Int8Array(); Int8Array(tiny_utf8::string name, std::vector data); Int8Array(tiny_utf8::string name, uint64_t length, int8_t data[]); ErrorOrVoid serializeWithoutHeader(std::vector* rawData) override; std::vector getData(); ErrorOr getValue(uint64_t position); void setData(std::vector newData); ErrorOrVoid setValue(uint64_t position, int8_t value); uint64_t length(); void addElement(int8_t element); ErrorOrVoid removeElement(uint64_t position); }; class String: public Generic { private: tiny_utf8::string value; public: String(); String(tiny_utf8::string name, tiny_utf8::string value); ErrorOrVoid serializeWithoutHeader(std::vector* rawData) override; tiny_utf8::string getValue(); void setValue(tiny_utf8::string value); }; class List: public Generic { private: std::vector tags; uint8_t containedType; public: List(); List(tiny_utf8::string name, uint8_t type); List(tiny_utf8::string name, std::vector data); ~List() override; ErrorOrVoid serializeWithoutHeader(std::vector* rawData) override; ErrorOr getElementPointer(uint64_t position); ErrorOrVoid setElementPointerAt(uint64_t position, Generic* pointer); ErrorOrVoid appendPointer(Generic* pointer); ErrorOrVoid deleteElement(uint64_t position); uint64_t length(); }; class Compound: public Generic { private: std::vector tags; public: Compound(); Compound(tiny_utf8::string name); Compound(tiny_utf8::string name, std::vector data); ~Compound() override; ErrorOrVoid serializeWithoutHeader(std::vector* rawData) override; ErrorOr getElementPointer(uint64_t position); ErrorOrVoid setElementPointerAt(uint64_t position, Generic* pointer); void appendPointer(Generic* pointer); ErrorOrVoid deleteElement(uint64_t position); uint64_t length(); }; class Int32Array: public Generic { private: std::vector data; public: Int32Array(); Int32Array(tiny_utf8::string name, std::vector data); Int32Array(tiny_utf8::string name, uint64_t length, int32_t data[]); ErrorOrVoid serializeWithoutHeader(std::vector* rawData) override; std::vector getData(); ErrorOr getValue(uint64_t position); void setData(std::vector newData); ErrorOrVoid setValue(uint64_t position, int32_t value); uint64_t length(); void addElement(int32_t element); ErrorOrVoid removeElement(uint64_t position); }; class Int64Array: public Generic { private: std::vector data; public: Int64Array(); Int64Array(tiny_utf8::string name, std::vector data); Int64Array(tiny_utf8::string name, uint64_t length, int64_t data[]); ErrorOrVoid serializeWithoutHeader(std::vector* rawData) override; std::vector getData(); ErrorOr getValue(uint64_t position); void setData(std::vector newData); ErrorOrVoid setValue(uint64_t position, int64_t value); uint64_t length(); void addElement(int64_t element); ErrorOrVoid removeElement(uint64_t position); }; } bool validateRawNBTData(uint8_t data[], uint64_t dataSize, uint64_t initialPosition=0, uint64_t* processedDataSize=nullptr); }