lib/nbt: Start implementing NBT data types, make NBT::Helper::writeString() return an error instead of aborting the program
The former is finally some progress on getting NBT going, though at this point it is all unvalidated work. For all I know it might cause your computer to burst into flames, grow an arm and a leg and an eye, and attack you with a sword. But probably, it won’t do that and instead start working after I have debugged it so much that I wish I could attack my PC with a sword. The latter is the implementation of a change I prepared in a previous commit when i added ErrorOrVoid.Soda
parent
398321e415
commit
10b1d9fa0c
|
@ -36,7 +36,6 @@ COMPILE_COMMANDS=(
|
|||
"$CXX_WITH_FLAGS src/test/nbt_read_write_helpers.cpp -I./include -Lbin/lib -l:nbt.so -l:javacompat.so -o bin/test/nbt_read_write_helpers"
|
||||
"$CXX_WITH_FLAGS src/test/cli_argument_parser.cpp -Lbin/lib -l:cli.so -o bin/test/cli_argument_parser"
|
||||
"$CXX_WITH_FLAGS src/test/javacompat.cpp -I./include -Lbin/lib -l:javacompat.so -o bin/test/javacompat"
|
||||
"$CXX_WITH_FLAGS src/test/nbt_write_string_failure_mode.cpp -I./include -Lbin/lib -l:nbt.so -l:javacompat.so -o bin/test/nbt_write_string_failure_mode"
|
||||
"$CXX_WITH_FLAGS src/test/nbt_tags.cpp -I./include -Lbin/lib -l:nbt.so -l:javacompat.so -o bin/test/nbt_tags"
|
||||
"$CXX_WITH_FLAGS src/test/nbt_size_helpers.cpp -I./include -Lbin/lib -l:nbt.so -l:javacompat.so -o bin/test/nbt_size_helpers"
|
||||
)
|
||||
|
|
247
src/lib/nbt.cpp
247
src/lib/nbt.cpp
|
@ -17,7 +17,6 @@
|
|||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <tinyutf8/tinyutf8.h>
|
||||
#include <iostream>
|
||||
|
||||
#include "nbt.hpp"
|
||||
#include "error.hpp"
|
||||
|
@ -360,13 +359,13 @@ namespace NBT {
|
|||
}
|
||||
}
|
||||
|
||||
void writeString(std::vector<uint8_t>* destination, tiny_utf8::string data) {
|
||||
ErrorOrVoid writeString(std::vector<uint8_t>* destination, tiny_utf8::string data) {
|
||||
ErrorOr<std::vector<uint8_t>> exportedString = JavaCompat::exportJavaString(data);
|
||||
if(exportedString.isError){
|
||||
std::cerr << "NBT::Helpers::writeString encountered an error: " << (int) exportedString.errorCode << std::endl;
|
||||
std::abort();
|
||||
return ErrorOrVoid(true, ErrorCodes::OVERRUN);
|
||||
}
|
||||
*destination = exportedString.value;
|
||||
return ErrorOrVoid();
|
||||
}
|
||||
|
||||
void writeInt32Array(std::vector<uint8_t>* destination, std::vector<int32_t> data) {
|
||||
|
@ -543,15 +542,241 @@ namespace NBT {
|
|||
}
|
||||
}
|
||||
|
||||
// generic class that all tag types are derived from
|
||||
template <typename T>
|
||||
Tag<T>::Tag() {
|
||||
namespace Tag {
|
||||
|
||||
}
|
||||
Generic::Generic() {
|
||||
this->type = TagType::INVALID;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
ErrorOr<std::vector<uint8_t>> Tag<T>::toRawData() {
|
||||
return ErrorOr<std::vector<uint8_t>>(true, ErrorCodes::INVALID_TYPE);
|
||||
Generic::~Generic() {}
|
||||
|
||||
ErrorOrVoid Generic::toRawData([[maybe_unused]] std::vector<uint8_t>* rawData) {
|
||||
return ErrorOrVoid(true, ErrorCodes::INVALID_TYPE);
|
||||
}
|
||||
|
||||
uint8_t Generic::getTagType(){
|
||||
return this->type;
|
||||
}
|
||||
|
||||
|
||||
End::End() {
|
||||
this->type = TagType::END;
|
||||
}
|
||||
|
||||
ErrorOrVoid End::toRawData(std::vector<uint8_t>* rawData) {
|
||||
rawData->push_back(this->type);
|
||||
return ErrorOrVoid();
|
||||
}
|
||||
|
||||
Int8::Int8() {
|
||||
this->type = TagType::INT8;
|
||||
}
|
||||
|
||||
Int8::Int8(tiny_utf8::string name, int8_t value) {
|
||||
this->type = TagType::INT8;
|
||||
this->name = name;
|
||||
this->value = value;
|
||||
}
|
||||
|
||||
ErrorOrVoid Int8::toRawData(std::vector<uint8_t>* rawData) {
|
||||
rawData->push_back(this->type);
|
||||
|
||||
if (Helper::writeString(rawData, this->name).isError) {
|
||||
return ErrorOrVoid(true, ErrorCodes::OVERRUN);
|
||||
}
|
||||
|
||||
Helper::writeInt8(rawData, this->value);
|
||||
|
||||
return ErrorOrVoid();
|
||||
}
|
||||
|
||||
int8_t Int8::getValue() {
|
||||
this->mutex.lock();
|
||||
int8_t value = this->value;
|
||||
this->mutex.unlock();
|
||||
return value;
|
||||
}
|
||||
|
||||
void Int8::setValue(int8_t value) {
|
||||
this->mutex.lock();
|
||||
this->value = value;
|
||||
this->mutex.unlock();
|
||||
}
|
||||
|
||||
Int16::Int16() {
|
||||
this->type = TagType::INT16;
|
||||
}
|
||||
|
||||
Int16::Int16(tiny_utf8::string name, int16_t value) {
|
||||
this->type = TagType::INT16;
|
||||
this->name = name;
|
||||
this->value = value;
|
||||
}
|
||||
|
||||
ErrorOrVoid Int16::toRawData(std::vector<uint8_t>* rawData) {
|
||||
rawData->push_back(this->type);
|
||||
|
||||
if (Helper::writeString(rawData, this->name).isError) {
|
||||
return ErrorOrVoid(true, ErrorCodes::OVERRUN);
|
||||
}
|
||||
|
||||
Helper::writeInt16(rawData, this->value);
|
||||
|
||||
return ErrorOrVoid();
|
||||
}
|
||||
|
||||
int16_t Int16::getValue() {
|
||||
this->mutex.lock();
|
||||
int16_t value = this->value;
|
||||
this->mutex.unlock();
|
||||
return value;
|
||||
}
|
||||
|
||||
void Int16::setValue(int16_t value) {
|
||||
this->mutex.lock();
|
||||
this->value = value;
|
||||
this->mutex.unlock();
|
||||
}
|
||||
|
||||
Int32::Int32() {
|
||||
this->type = TagType::INT32;
|
||||
}
|
||||
|
||||
Int32::Int32(tiny_utf8::string name, int32_t value) {
|
||||
this->type = TagType::INT32;
|
||||
this->name = name;
|
||||
this->value = value;
|
||||
}
|
||||
|
||||
ErrorOrVoid Int32::toRawData(std::vector<uint8_t>* rawData) {
|
||||
rawData->push_back(this->type);
|
||||
|
||||
if (Helper::writeString(rawData, this->name).isError) {
|
||||
return ErrorOrVoid(true, ErrorCodes::OVERRUN);
|
||||
}
|
||||
|
||||
Helper::writeInt32(rawData, this->value);
|
||||
|
||||
return ErrorOrVoid();
|
||||
}
|
||||
|
||||
int32_t Int32::getValue() {
|
||||
this->mutex.lock();
|
||||
int32_t value = this->value;
|
||||
this->mutex.unlock();
|
||||
return value;
|
||||
}
|
||||
|
||||
void Int32::setValue(int32_t value) {
|
||||
this->mutex.lock();
|
||||
this->value = value;
|
||||
this->mutex.unlock();
|
||||
}
|
||||
|
||||
Int64::Int64() {
|
||||
this->type = TagType::INT64;
|
||||
}
|
||||
|
||||
Int64::Int64(tiny_utf8::string name, int64_t value) {
|
||||
this->type = TagType::INT64;
|
||||
this->name = name;
|
||||
this->value = value;
|
||||
}
|
||||
|
||||
ErrorOrVoid Int64::toRawData(std::vector<uint8_t>* rawData) {
|
||||
rawData->push_back(this->type);
|
||||
|
||||
if (Helper::writeString(rawData, this->name).isError) {
|
||||
return ErrorOrVoid(true, ErrorCodes::OVERRUN);
|
||||
}
|
||||
|
||||
Helper::writeInt64(rawData, this->value);
|
||||
|
||||
return ErrorOrVoid();
|
||||
}
|
||||
|
||||
int64_t Int64::getValue() {
|
||||
this->mutex.lock();
|
||||
int64_t value = this->value;
|
||||
this->mutex.unlock();
|
||||
return value;
|
||||
}
|
||||
|
||||
void Int64::setValue(int64_t value) {
|
||||
this->mutex.lock();
|
||||
this->value = value;
|
||||
this->mutex.unlock();
|
||||
}
|
||||
|
||||
Float::Float() {
|
||||
this->type = TagType::FLOAT;
|
||||
}
|
||||
|
||||
Float::Float(tiny_utf8::string name, float value) {
|
||||
this->type = TagType::FLOAT;
|
||||
this->name = name;
|
||||
this->value = value;
|
||||
}
|
||||
|
||||
ErrorOrVoid Float::toRawData(std::vector<uint8_t>* rawData) {
|
||||
rawData->push_back(this->type);
|
||||
|
||||
if (Helper::writeString(rawData, this->name).isError) {
|
||||
return ErrorOrVoid(true, ErrorCodes::OVERRUN);
|
||||
}
|
||||
|
||||
Helper::writeFloat(rawData, this->value);
|
||||
|
||||
return ErrorOrVoid();
|
||||
}
|
||||
|
||||
float Float::getValue() {
|
||||
this->mutex.lock();
|
||||
float value = this->value;
|
||||
this->mutex.unlock();
|
||||
return value;
|
||||
}
|
||||
|
||||
void Float::setValue(float value) {
|
||||
this->mutex.lock();
|
||||
this->value = value;
|
||||
this->mutex.unlock();
|
||||
}
|
||||
|
||||
Double::Double() {
|
||||
this->type = TagType::DOUBLE;
|
||||
}
|
||||
|
||||
Double::Double(tiny_utf8::string name, double value) {
|
||||
this->type = TagType::DOUBLE;
|
||||
this->name = name;
|
||||
this->value = value;
|
||||
}
|
||||
|
||||
ErrorOrVoid Double::toRawData(std::vector<uint8_t>* rawData) {
|
||||
rawData->push_back(this->type);
|
||||
|
||||
if (Helper::writeString(rawData, this->name).isError) {
|
||||
return ErrorOrVoid(true, ErrorCodes::OVERRUN);
|
||||
}
|
||||
|
||||
Helper::writeDouble(rawData, this->value);
|
||||
|
||||
return ErrorOrVoid();
|
||||
}
|
||||
|
||||
double Double::getValue() {
|
||||
this->mutex.lock();
|
||||
double value = this->value;
|
||||
this->mutex.unlock();
|
||||
return value;
|
||||
}
|
||||
|
||||
void Double::setValue(double value) {
|
||||
this->mutex.lock();
|
||||
this->value = value;
|
||||
this->mutex.unlock();
|
||||
}
|
||||
}
|
||||
|
||||
bool validateRawListContents(uint8_t data[], uint64_t dataSize, uint64_t initialPosition, uint64_t* processedDataSize) {
|
||||
|
|
109
src/lib/nbt.hpp
109
src/lib/nbt.hpp
|
@ -38,6 +38,8 @@
|
|||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <tinyutf8/tinyutf8.h>
|
||||
#include <mutex>
|
||||
|
||||
#include "error.hpp"
|
||||
|
||||
namespace NBT {
|
||||
|
@ -61,7 +63,7 @@ namespace NBT {
|
|||
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);
|
||||
void writeString(std::vector<uint8_t>* destination, tiny_utf8::string data);
|
||||
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);
|
||||
|
@ -87,20 +89,103 @@ namespace NBT {
|
|||
const uint8_t INT64_ARRAY= 12;
|
||||
// This is a workaround that's not part of the spec.
|
||||
const uint8_t INVALID = 255;
|
||||
|
||||
// This class is used as a placeholder for implementing the end tag.
|
||||
class End {};
|
||||
}
|
||||
|
||||
// generic class that all tag types are derived from
|
||||
template <typename T>
|
||||
struct Tag {
|
||||
const uint8_t type = TagType::INVALID;
|
||||
T* containedData;
|
||||
namespace Tag {
|
||||
|
||||
Tag();
|
||||
ErrorOr<std::vector<uint8_t>> toRawData();
|
||||
};
|
||||
class Generic {
|
||||
protected:
|
||||
std::mutex mutex;
|
||||
uint8_t type;
|
||||
public:
|
||||
tiny_utf8::string name;
|
||||
|
||||
Generic();
|
||||
virtual ~Generic();
|
||||
|
||||
virtual ErrorOrVoid toRawData(std::vector<uint8_t>* rawData);
|
||||
uint8_t getTagType();
|
||||
};
|
||||
|
||||
class End: public Generic {
|
||||
public:
|
||||
End();
|
||||
|
||||
ErrorOrVoid toRawData(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 toRawData(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 toRawData(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 toRawData(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 toRawData(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 toRawData(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 toRawData(std::vector<uint8_t>* rawData) override;
|
||||
double getValue();
|
||||
void setValue(double value);
|
||||
};
|
||||
}
|
||||
|
||||
bool validateRawNBTData(uint8_t data[], uint64_t dataSize, uint64_t initialPosition=0, uint64_t* processedDataSize=nullptr);
|
||||
}
|
||||
|
|
|
@ -573,6 +573,11 @@ int main(){
|
|||
NBT::Helper::writeString(exportedString, normalString);
|
||||
ASSERT(javaStdString1 == *exportedString);
|
||||
|
||||
//check that we get an error when trying to write a string that is too long
|
||||
std::string overrunString = std::string(0xFFFFF, '.');
|
||||
ASSERT(NBT::Helper::writeString(exportedString, tiny_utf8::string(overrunString)).isError);
|
||||
ASSERT(NBT::Helper::writeString(exportedString, tiny_utf8::string(overrunString)).errorCode == ErrorCodes::OVERRUN);
|
||||
|
||||
std::cout << "Passed writeString NBT helper test." << std::endl;
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -30,15 +30,15 @@ int main(){
|
|||
std::cout << "################################################################################" << std::endl;
|
||||
|
||||
//Byte tag constructor test
|
||||
uint8_t bytetest[] = {0x01, 0x00, 0x02, 0x68, 0x69, 0x32};
|
||||
NBT::Byte byte = NBT::Byte(bytetest);
|
||||
//uint8_t bytetest[] = {0x01, 0x00, 0x02, 0x68, 0x69, 0x32};
|
||||
//NBT::Byte byte = NBT::Byte(bytetest);
|
||||
|
||||
ASSERT(byte.tagType == 1);
|
||||
ASSERT(byte.nameSize == 2);
|
||||
ASSERT(byte.content = 0x32);
|
||||
ASSERT(byte.name == tiny_utf8::string("hi"));
|
||||
//ASSERT(byte.tagType == 1);
|
||||
//ASSERT(byte.nameSize == 2);
|
||||
//ASSERT(byte.content = 0x32);
|
||||
//ASSERT(byte.name == tiny_utf8::string("hi"));
|
||||
|
||||
std::cout << "Passed Byte Tag constructor test." << std::endl;
|
||||
//std::cout << "Passed Byte Tag constructor test." << std::endl;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -1,31 +0,0 @@
|
|||
// 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
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <iostream>
|
||||
#include "../lib/nbt.hpp"
|
||||
#include "../lib/error.hpp"
|
||||
#include "../lib/javacompat.hpp"
|
||||
|
||||
int main() {
|
||||
std::cout << "================================================================================" << std::endl;
|
||||
std::cout << "NBT write string helper failure mode test" << std::endl;
|
||||
std::cout << "================================================================================" << std::endl;
|
||||
std::cout << "This is supposed to abort." << std::endl;
|
||||
std::vector<uint8_t>* exportedString = new std::vector<uint8_t>();
|
||||
std::string overrunString = std::string(0xFFFFF, '.');
|
||||
NBT::Helper::writeString(exportedString, tiny_utf8::string(overrunString));
|
||||
}
|
Loading…
Reference in New Issue