diff --git a/scripts/test.sh b/scripts/test.sh index 5dbded7..0945fdf 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -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" "$CXX_WITH_FLAGS src/test/file.cpp -I./include -Lbin/lib -l:file.so -o bin/test/file" diff --git a/src/lib/nbt.cpp b/src/lib/nbt.cpp index 17e5352..9e66fee 100644 --- a/src/lib/nbt.cpp +++ b/src/lib/nbt.cpp @@ -17,7 +17,6 @@ #include #include #include -#include #include "nbt.hpp" #include "error.hpp" @@ -360,13 +359,13 @@ namespace NBT { } } - void writeString(std::vector* destination, tiny_utf8::string data) { + ErrorOrVoid writeString(std::vector* destination, tiny_utf8::string data) { ErrorOr> 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* destination, std::vector data) { @@ -543,15 +542,307 @@ namespace NBT { } } - // generic class that all tag types are derived from - template - Tag::Tag() { + namespace Tag { - } + Generic::Generic() { + this->type = TagType::INVALID; + } - template - ErrorOr> Tag::toRawData() { - return ErrorOr>(true, ErrorCodes::INVALID_TYPE); + Generic::~Generic() {} + + ErrorOrVoid Generic::toRawData([[maybe_unused]] std::vector* 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* 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* 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* 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* 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* 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* 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* 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(); + } + + Int8Array::Int8Array() { + this->type = TagType::INT8_ARRAY; + } + + Int8Array::Int8Array(tiny_utf8::string name, std::vector data) { + this->type = TagType::INT8_ARRAY; + this->name = name; + this->data = data; + } + Int8Array::Int8Array(tiny_utf8::string name, uint64_t length, int8_t data[]){ + this->type = TagType::INT8_ARRAY; + this->name = name; + this->data = std::vector(data, data+length); + } + + ErrorOrVoid Int8Array::toRawData(std::vector* rawData) { + rawData->push_back(this->type); + + if (Helper::writeString(rawData, this->name).isError) { + return ErrorOrVoid(true, ErrorCodes::OVERRUN); + } + + Helper::writeInt8Array(rawData, this->data); + + return ErrorOrVoid(); + } + + std::vector Int8Array::getData() { + return this->data; + } + + ErrorOr Int8Array::getValue(uint64_t position) { + if (this->data.size() <= position) { + return ErrorOr(this->data.at(position)); + } + + return ErrorOr(true, ErrorCodes::OUT_OF_RANGE); + } + + void Int8Array::setData(std::vector newData) { + this->data = newData; + } + + ErrorOrVoid Int8Array::setValue(uint64_t position, int8_t value) { + if (this->data.size() <= position) { + return ErrorOrVoid(true, ErrorCodes::OUT_OF_RANGE); + } + + this->data[position] = value; + + return ErrorOrVoid(); + } + + uint64_t Int8Array::length() { + return this->data.size(); + } + + void Int8Array::addElement(int8_t element) { + this->data.push_back(element); + } + + ErrorOrVoid Int8Array::removeElement(uint64_t position) { + //TODO: implement + //this->data.erase(position); + } } bool validateRawListContents(uint8_t data[], uint64_t dataSize, uint64_t initialPosition, uint64_t* processedDataSize) { diff --git a/src/lib/nbt.hpp b/src/lib/nbt.hpp index eae5ed9..f9a3887 100644 --- a/src/lib/nbt.hpp +++ b/src/lib/nbt.hpp @@ -38,6 +38,8 @@ #include #include #include +#include + #include "error.hpp" namespace NBT { @@ -61,7 +63,7 @@ namespace NBT { 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); - void writeString(std::vector* destination, tiny_utf8::string data); + 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); @@ -87,20 +89,211 @@ 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 - struct Tag { - const uint8_t type = TagType::INVALID; - T* containedData; + namespace Tag { - Tag(); - ErrorOr> toRawData(); - }; + class Generic { + protected: + std::mutex mutex; + uint8_t type; + public: + tiny_utf8::string name; + + Generic(); + virtual ~Generic(); + + virtual ErrorOrVoid toRawData(std::vector* rawData); + uint8_t getTagType(); + }; + + class End: public Generic { + public: + End(); + + ErrorOrVoid toRawData(std::vector* rawData) override; + }; + + class Int8: public Generic { + private: + int8_t value; + public: + Int8(); + Int8(tiny_utf8::string name, int8_t value); + + ErrorOrVoid toRawData(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 toRawData(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 toRawData(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 toRawData(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 toRawData(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 toRawData(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 toRawData(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 toRawData(std::vector* rawData) override; + tiny_utf8::string getValue(); + void setValue(tiny_utf8::string value); + }; + + class List: public Generic { + private: + std::vector tags; + uint8_t type; + public: + List(); + List(tiny_utf8::string name, uint8_t type); + List(tiny_utf8::string name, std::vector data); + + ~List() override; + + ErrorOrVoid toRawData(std::vector* rawData) override; + + ErrorOr getElementPointer(uint64_t position); + ErrorOrVoid setElementPointerAt(uint64_t position, Generic*); + ErrorOrVoid appendPointer(Generic*); + 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 toRawData(std::vector* rawData) override; + + ErrorOr getElementPointer(uint64_t position); + ErrorOrVoid setElementPointerAt(uint64_t position, Generic*); + ErrorOrVoid appendPointer(Generic*); + 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 toRawData(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 toRawData(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); } diff --git a/src/test/nbt_read_write_helpers.cpp b/src/test/nbt_read_write_helpers.cpp index 3f85d9a..323fea8 100644 --- a/src/test/nbt_read_write_helpers.cpp +++ b/src/test/nbt_read_write_helpers.cpp @@ -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(0x10000, '.'); + 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; diff --git a/src/test/nbt_tags.cpp b/src/test/nbt_tags.cpp index 7da49f8..ec839fa 100644 --- a/src/test/nbt_tags.cpp +++ b/src/test/nbt_tags.cpp @@ -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; } diff --git a/src/test/nbt_write_string_failure_mode.cpp b/src/test/nbt_write_string_failure_mode.cpp deleted file mode 100644 index cbc8367..0000000 --- a/src/test/nbt_write_string_failure_mode.cpp +++ /dev/null @@ -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 -#include -#include -#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* exportedString = new std::vector(); - std::string overrunString = std::string(0xFFFFF, '.'); - NBT::Helper::writeString(exportedString, tiny_utf8::string(overrunString)); -}