// 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 representation: Tag(uint8:tag_type, String:name, uint16:name_size, byte[]:content, int32:size) // None (compound 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 // float32: Tag( 5, String:name, uint16:name_size, float32:content,4) => 32 bit IEEE754 floating point number, size not stored // float64: Tag( 6, String:name, uint16:name_size, float64:content,8) => 64 bit IEEE754 floating point number, size not stored // int8[]: Tag( 7, String:name, uint16:name_size, int8[]:content, int32:size) => 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 // Tag[] (list): Tag( 9, String:name, uint16:name_size, Tag[]:content, int32:size) => list of tags of the same type with tag type and name information omitted prefixed by (in order) content type and size // Tag[] (compound): Tag(10, String:name, uint16:name_size, Tag[]:content, int32:size) => list of tags, last tag is always an end tag, size not stored // int32[]: Tag(11, String:name, uint16:name_size, int32[]:content,int32:size) => list of 32 bit signed integers prefixed with its size, endianness not verified at this point // int64[]: Tag(12, String:name, uint16:name_size, int64[]:content,int32:size) => list of 64 bit signed integers prefixed with its size, endianness not verified at this point #pragma once #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); //FIXME: we just assume that float is a single-precision IEEE754 // floating point number ErrorOr readFloat32(uint8_t data[], uint64_t dataSize, uint64_t currentPosition); //FIXME: we just assume that double is a double-precision IEEE754 // floating point number ErrorOr readFloat64(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); //FIXME: we just assume that float is a single-precision IEEE754 // floating point number void writeFloat32(std::vector* destination, float data); //FIXME: we just assume that double is a single-precision IEEE754 // floating point number void writeFloat64(std::vector* destination, double data); void writeInt8Array(std::vector* destination, std::vector data); void writeInt8Array(std::vector* destination, int8_t data[], uint32_t dataSize); void 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); } //Generic parent class to make declaration easier template class Tag{ public: uint8_t tagType; tiny_utf8::string name; uint16_t nameSize; T content; int32_t size; Tag(){} Tag(uint8_t tagType, tiny_utf8::string name, uint16_t nameSize, T content, uint32_t size); }; class End: public Tag{ public: End(); }; class Byte: public Tag{ public: Byte(tiny_utf8::string name, uint16_t nameSize, int8_t content); Byte(uint8_t data[]); bool validate(uint8_t data[]); }; bool validateRawNBTData(uint8_t data[], int length); }