307 lines
14 KiB
C++
307 lines
14 KiB
C++
// 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: <type><name size><name><payload>. 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<Tag:type>( 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 <cstdint>
|
|
#include <vector>
|
|
#include <tinyutf8/tinyutf8.h>
|
|
#include <mutex>
|
|
|
|
#include "error.hpp"
|
|
|
|
namespace NBT {
|
|
namespace Helper {
|
|
ErrorOr<int8_t> readInt8(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
|
|
ErrorOr<int16_t> readInt16(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
|
|
ErrorOr<int32_t> readInt32(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
|
|
ErrorOr<int64_t> readInt64(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
|
|
ErrorOr<float> readFloat(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
|
|
ErrorOr<double> readDouble(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
|
|
ErrorOr<std::vector<int8_t>> readInt8Array(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
|
|
ErrorOr<tiny_utf8::string> readString(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
|
|
ErrorOr<std::vector<int32_t>> readInt32Array(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
|
|
ErrorOr<std::vector<int64_t>> readInt64Array(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
|
|
|
|
void writeInt8(std::vector<uint8_t>* destination, int8_t data);
|
|
void writeInt16(std::vector<uint8_t>* destination, int16_t data);
|
|
void writeInt32(std::vector<uint8_t>* destination, int32_t data);
|
|
void writeInt64(std::vector<uint8_t>* destination, int64_t data);
|
|
void writeFloat(std::vector<uint8_t>* destination, float data);
|
|
void writeDouble(std::vector<uint8_t>* destination, double data);
|
|
void writeInt8Array(std::vector<uint8_t>* destination, std::vector<int8_t> data);
|
|
void writeInt8Array(std::vector<uint8_t>* destination, int8_t data[], uint32_t dataSize);
|
|
ErrorOrVoid writeString(std::vector<uint8_t>* destination, tiny_utf8::string data);
|
|
void writeInt32Array(std::vector<uint8_t>* destination, std::vector<int32_t> data);
|
|
void writeInt32Array(std::vector<uint8_t>* destination, int32_t data[], uint32_t dataSize);
|
|
void writeInt64Array(std::vector<uint8_t>* destination, std::vector<int64_t> data);
|
|
void writeInt64Array(std::vector<uint8_t>* destination, int64_t data[], uint32_t dataSize);
|
|
|
|
ErrorOr<uint64_t> totalTagSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
|
|
ErrorOr<int32_t> 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<uint8_t>* rawData);
|
|
virtual ErrorOrVoid serializeWithoutHeader(std::vector<uint8_t>* 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<uint8_t>* rawData) override;
|
|
ErrorOrVoid serializeWithoutHeader(std::vector<uint8_t>* rawData) override;
|
|
};
|
|
|
|
class Int8: public Generic {
|
|
private:
|
|
int8_t value;
|
|
public:
|
|
Int8();
|
|
Int8(tiny_utf8::string name, int8_t value);
|
|
|
|
ErrorOrVoid serializeWithoutHeader(std::vector<uint8_t>* 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<uint8_t>* 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<uint8_t>* 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<uint8_t>* 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<uint8_t>* 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<uint8_t>* rawData) override;
|
|
double getValue();
|
|
void setValue(double value);
|
|
};
|
|
|
|
class Int8Array: public Generic {
|
|
private:
|
|
std::vector<int8_t> data;
|
|
public:
|
|
Int8Array();
|
|
Int8Array(tiny_utf8::string name, std::vector<int8_t> data);
|
|
Int8Array(tiny_utf8::string name, uint64_t length, int8_t data[]);
|
|
|
|
ErrorOrVoid serializeWithoutHeader(std::vector<uint8_t>* rawData) override;
|
|
|
|
std::vector<int8_t> getData();
|
|
ErrorOr<int8_t> getValue(uint64_t position);
|
|
void setData(std::vector<int8_t> 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<uint8_t>* rawData) override;
|
|
tiny_utf8::string getValue();
|
|
void setValue(tiny_utf8::string value);
|
|
};
|
|
|
|
class List: public Generic {
|
|
private:
|
|
std::vector<Generic*> tags;
|
|
uint8_t containedType;
|
|
public:
|
|
List();
|
|
List(tiny_utf8::string name, uint8_t type);
|
|
List(tiny_utf8::string name, std::vector<Generic*> data);
|
|
|
|
~List() override;
|
|
|
|
ErrorOrVoid serializeWithoutHeader(std::vector<uint8_t>* rawData) override;
|
|
|
|
ErrorOr<Generic*> 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<Generic*> tags;
|
|
// built-in end tag
|
|
End* endPointer;
|
|
public:
|
|
Compound();
|
|
Compound(tiny_utf8::string name);
|
|
Compound(tiny_utf8::string name, std::vector<Generic*> data);
|
|
|
|
~Compound() override;
|
|
|
|
ErrorOrVoid serializeWithoutHeader(std::vector<uint8_t>* rawData) override;
|
|
|
|
ErrorOr<Generic*> getElementPointer(uint64_t position);
|
|
ErrorOrVoid setElementPointerAt(uint64_t position, Generic* pointer);
|
|
ErrorOrVoid appendPointer(Generic* pointer);
|
|
ErrorOrVoid deleteElement(uint64_t position);
|
|
uint64_t length();
|
|
};
|
|
|
|
class Int32Array: public Generic {
|
|
private:
|
|
std::vector<int32_t> data;
|
|
public:
|
|
Int32Array();
|
|
Int32Array(tiny_utf8::string name, std::vector<int32_t> data);
|
|
Int32Array(tiny_utf8::string name, uint64_t length, int32_t data[]);
|
|
|
|
ErrorOrVoid serializeWithoutHeader(std::vector<uint8_t>* rawData) override;
|
|
|
|
std::vector<int32_t> getData();
|
|
ErrorOr<int32_t> getValue(uint64_t position);
|
|
void setData(std::vector<int32_t> 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<int64_t> data;
|
|
public:
|
|
Int64Array();
|
|
Int64Array(tiny_utf8::string name, std::vector<int64_t> data);
|
|
Int64Array(tiny_utf8::string name, uint64_t length, int64_t data[]);
|
|
|
|
ErrorOrVoid serializeWithoutHeader(std::vector<uint8_t>* rawData) override;
|
|
|
|
std::vector<int64_t> getData();
|
|
ErrorOr<int64_t> getValue(uint64_t position);
|
|
void setData(std::vector<int64_t> newData);
|
|
ErrorOrVoid setValue(uint64_t position, int64_t value);
|
|
uint64_t length();
|
|
void addElement(int64_t element);
|
|
ErrorOrVoid removeElement(uint64_t position);
|
|
};
|
|
}
|
|
|
|
ErrorOr<std::vector<Tag::Generic*>> deserialize(uint8_t data[], uint64_t dataSize, uint64_t initialPosition=0, uint64_t* processedDataSize=nullptr);
|
|
bool validateRawNBTData(uint8_t data[], uint64_t dataSize, uint64_t initialPosition=0, uint64_t* processedDataSize=nullptr);
|
|
}
|