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" | ||||
| ) | ||||
|  |  | |||
							
								
								
									
										245
									
								
								src/lib/nbt.cpp
								
								
								
								
							
							
						
						
									
										245
									
								
								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) { | ||||
|  |  | |||
							
								
								
									
										107
									
								
								src/lib/nbt.hpp
								
								
								
								
							
							
						
						
									
										107
									
								
								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
	
	 BodgeMaster
						BodgeMaster