// 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 #include #include "nbt.hpp" #include "error.hpp" #include "javacompat.hpp" #include "../../.endianness" #ifdef FOSSVG_ENDIAN_BIG_WORD #error "Honeywell-316-style endianness is not supported. If you feel like it should, feel free to participate in the project to maintain it." #endif #ifdef FOSSVG_ENDIAN_LITTLE_WORD #error "PDP-11-style endianness is not supported. If you feel like it should, feel free to participate in the project to maintain it." #endif #ifdef FOSSVG_ENDIAN_UNKNOWN #error "The endianness of your system could not be determined. Please set it manually. FOSS-VG is currently implemented using some endian-specific functions." #endif namespace NBT { namespace helper { ErrorOr readInt8(uint8_t data[], uint64_t dataSize, uint64_t currentPosition) { if (dataSize(true, ErrorCodes::OUT_OF_RANGE); return ErrorOr((int8_t) data[currentPosition]); } ErrorOr readInt16(uint8_t data[], uint64_t dataSize, uint64_t currentPosition) { if (dataSize(true, ErrorCodes::OUT_OF_RANGE); return ErrorOr((int16_t) ((static_cast(data[currentPosition]) << 8) | static_cast(data[currentPosition+1]))); } ErrorOr readInt32(uint8_t data[], uint64_t dataSize, uint64_t currentPosition) { if (dataSize(true, ErrorCodes::OUT_OF_RANGE); return ErrorOr((int32_t) ( (static_cast(data[currentPosition ]) << 24) | (static_cast(data[currentPosition+1]) << 16) | (static_cast(data[currentPosition+2]) << 8) | static_cast(data[currentPosition+3]) )); } ErrorOr readInt64(uint8_t data[], uint64_t dataSize, uint64_t currentPosition) { if (dataSize(true, ErrorCodes::OUT_OF_RANGE); return ErrorOr((int64_t) ( (static_cast(data[currentPosition ]) << 56) | (static_cast(data[currentPosition+1]) << 48) | (static_cast(data[currentPosition+2]) << 40) | (static_cast(data[currentPosition+3]) << 32) | (static_cast(data[currentPosition+4]) << 24) | (static_cast(data[currentPosition+5]) << 16) | (static_cast(data[currentPosition+6]) << 8) | static_cast(data[currentPosition+7]) )); } //FIXME: endian-dependent implementations ErrorOr readFloat(uint8_t data[], uint64_t dataSize, uint64_t currentPosition) { float* value = new float; uint8_t* valueAsBytes = reinterpret_cast(value); if (dataSize<=currentPosition) return ErrorOr(true, ErrorCodes::OUT_OF_RANGE); if (dataSize(true, ErrorCodes::OVERRUN); #ifdef FOSSVG_BIG_ENDIAN *valueAsBytes = data[currentPosition]; *(valueAsBytes+1) = data[currentPosition+1]; *(valueAsBytes+2) = data[currentPosition+2]; *(valueAsBytes+3) = data[currentPosition+3]; #else #ifdef FOSSVG_LITTLE_ENDIAN *valueAsBytes = data[currentPosition+3]; *(valueAsBytes+1) = data[currentPosition+2]; *(valueAsBytes+2) = data[currentPosition+1]; *(valueAsBytes+3) = data[currentPosition]; #else #error "NBT::helper::readFloat: An implementation for your endianness is unavailable." #endif #endif float dereferencedValue = *value; delete value; return ErrorOr(dereferencedValue); } //FIXME: endian-dependent implementations ErrorOr readDouble(uint8_t data[], uint64_t dataSize, uint64_t currentPosition) { double* value = new double; uint8_t* valueAsBytes = reinterpret_cast(value); if (dataSize<=currentPosition) return ErrorOr(true, ErrorCodes::OUT_OF_RANGE); if (dataSize(true, ErrorCodes::OVERRUN); #ifdef FOSSVG_BIG_ENDIAN *valueAsBytes = data[currentPosition]; *(valueAsBytes+1) = data[currentPosition+1]; *(valueAsBytes+2) = data[currentPosition+2]; *(valueAsBytes+3) = data[currentPosition+3]; *(valueAsBytes+4) = data[currentPosition+4]; *(valueAsBytes+5) = data[currentPosition+5]; *(valueAsBytes+6) = data[currentPosition+6]; *(valueAsBytes+7) = data[currentPosition+7]; #else #ifdef FOSSVG_LITTLE_ENDIAN *valueAsBytes = data[currentPosition+7]; *(valueAsBytes+1) = data[currentPosition+6]; *(valueAsBytes+2) = data[currentPosition+5]; *(valueAsBytes+3) = data[currentPosition+4]; *(valueAsBytes+4) = data[currentPosition+3]; *(valueAsBytes+5) = data[currentPosition+2]; *(valueAsBytes+6) = data[currentPosition+1]; *(valueAsBytes+7) = data[currentPosition]; #else #error "NBT::helper::readDouble: An implementation for your endianness is unavailable." #endif #endif double dereferencedValue = *value; delete value; return ErrorOr(dereferencedValue); } ErrorOr> readInt8Array(uint8_t data[], uint64_t dataSize, uint64_t currentPosition) { // get size prefix ErrorOr size = readInt32(data, dataSize, currentPosition); if (size.isError) return ErrorOr>(true, size.errorCode); // get content if (currentPosition+4+size.value > dataSize) return ErrorOr>(true, ErrorCodes::OVERRUN); std::vector result = std::vector(); for (int i=0; i>(result); } ErrorOr readString(uint8_t data[], uint64_t dataSize, uint64_t currentPosition) { if(dataSize > 0xFFFF){ return ErrorOr(true, ErrorCodes::OVERRUN); } ErrorOr output = JavaCompat::importJavaString(data+currentPosition, (uint16_t) dataSize); if(output.isError){ return ErrorOr(true, output.errorCode); } return output; } ErrorOr> readInt32Array(uint8_t data[], uint64_t dataSize, uint64_t currentPosition) { // get size prefix ErrorOr size = readInt32(data, dataSize, currentPosition); if (size.isError) return ErrorOr>(true, size.errorCode); // get content if (currentPosition+4+(size.value*4) > dataSize) return ErrorOr>(true, ErrorCodes::OVERRUN); std::vector result = std::vector(); for (int i=0; i nextInt32 = readInt32(data, dataSize, currentPosition+4+(i*4)); if (nextInt32.isError) return ErrorOr>(true, nextInt32.errorCode); result.push_back(nextInt32.value); } return ErrorOr>(result); } ErrorOr> readInt64Array(uint8_t data[], uint64_t dataSize, uint64_t currentPosition) { // get size prefix ErrorOr size = readInt32(data, dataSize, currentPosition); if (size.isError) return ErrorOr>(true, size.errorCode); // get content if (currentPosition+4+(size.value*8) > dataSize) return ErrorOr>(true, ErrorCodes::OVERRUN); std::vector result = std::vector(); for (int i=0; i nextInt64 = readInt64(data, dataSize, currentPosition+4+(i*8)); if (nextInt64.isError) return ErrorOr>(true, nextInt64.errorCode); result.push_back(nextInt64.value); } return ErrorOr>(result); } void writeInt8(std::vector* destination, int8_t data) { destination->push_back((uint8_t) data); } //FIXME: endian dependent implementation void writeInt16(std::vector* destination, int16_t data) { int16_t* value = new int16_t; uint8_t* valueAsBytes = reinterpret_cast(value); *value = data; #ifdef FOSSVG_BIG_ENDIAN destination->push_back(*valueAsBytes); destination->push_back(*(valueAsBytes+1)); #else #ifdef FOSSVG_LITTLE_ENDIAN destination->push_back(*(valueAsBytes+1)); destination->push_back(*valueAsBytes); #else #error "NBT::helper::writeInt16: An implementation for your endianness is unavailable." #endif #endif delete value; } //FIXME: endian dependent implementation void writeInt32(std::vector* destination, int32_t data) { int32_t* value = new int32_t; uint8_t* valueAsBytes = reinterpret_cast(value); *value = data; #ifdef FOSSVG_BIG_ENDIAN destination->push_back(*valueAsBytes); destination->push_back(*(valueAsBytes+1)); destination->push_back(*(valueAsBytes+2)); destination->push_back(*(valueAsBytes+3)); #else #ifdef FOSSVG_LITTLE_ENDIAN destination->push_back(*(valueAsBytes+3)); destination->push_back(*(valueAsBytes+2)); destination->push_back(*(valueAsBytes+1)); destination->push_back(*valueAsBytes); #else #error "NBT::helper::writeInt16: An implementation for your endianness is unavailable." #endif #endif delete value; } //FIXME: endian dependent implementation void writeInt64(std::vector* destination, int64_t data) { int64_t* value = new int64_t; uint8_t* valueAsBytes = reinterpret_cast(value); *value = data; #ifdef FOSSVG_BIG_ENDIAN destination->push_back(*valueAsBytes); destination->push_back(*(valueAsBytes+1)); destination->push_back(*(valueAsBytes+2)); destination->push_back(*(valueAsBytes+3)); destination->push_back(*(valueAsBytes+4)); destination->push_back(*(valueAsBytes+5)); destination->push_back(*(valueAsBytes+6)); destination->push_back(*(valueAsBytes+7)); #else #ifdef FOSSVG_LITTLE_ENDIAN destination->push_back(*(valueAsBytes+7)); destination->push_back(*(valueAsBytes+6)); destination->push_back(*(valueAsBytes+5)); destination->push_back(*(valueAsBytes+4)); destination->push_back(*(valueAsBytes+3)); destination->push_back(*(valueAsBytes+2)); destination->push_back(*(valueAsBytes+1)); destination->push_back(*valueAsBytes); #else #error "NBT::helper::writeInt16: An implementation for your endianness is unavailable." #endif #endif delete value; } //FIXME: endian-specific implementations void writeFloat(std::vector* destination, float data) { float* value = new float; uint8_t* valueAsBytes = reinterpret_cast(value); *value = data; #ifdef FOSSVG_BIG_ENDIAN destination->push_back(*valueAsBytes); destination->push_back(*(valueAsBytes+1)); destination->push_back(*(valueAsBytes+2)); destination->push_back(*(valueAsBytes+3)); #else #ifdef FOSSVG_LITTLE_ENDIAN destination->push_back(*(valueAsBytes+3)); destination->push_back(*(valueAsBytes+2)); destination->push_back(*(valueAsBytes+1)); destination->push_back(*valueAsBytes); #else #error "NBT::helper::writeInt16: An implementation for your endianness is unavailable." #endif #endif delete value; } //FIXME: endian-specific implementations void writeDouble(std::vector* destination, double data) { double* value = new double; uint8_t* valueAsBytes = reinterpret_cast(value); *value = data; #ifdef FOSSVG_BIG_ENDIAN destination->push_back(*valueAsBytes); destination->push_back(*(valueAsBytes+1)); destination->push_back(*(valueAsBytes+2)); destination->push_back(*(valueAsBytes+3)); destination->push_back(*(valueAsBytes+4)); destination->push_back(*(valueAsBytes+5)); destination->push_back(*(valueAsBytes+6)); destination->push_back(*(valueAsBytes+7)); #else #ifdef FOSSVG_LITTLE_ENDIAN destination->push_back(*(valueAsBytes+7)); destination->push_back(*(valueAsBytes+6)); destination->push_back(*(valueAsBytes+5)); destination->push_back(*(valueAsBytes+4)); destination->push_back(*(valueAsBytes+3)); destination->push_back(*(valueAsBytes+2)); destination->push_back(*(valueAsBytes+1)); destination->push_back(*valueAsBytes); #else #error "NBT::helper::writeInt16: An implementation for your endianness is unavailable." #endif #endif delete value; } void writeInt8Array(std::vector* destination, std::vector data) { writeInt32(destination, data.size()); for(int8_t datum: data){ destination->push_back(datum); } } void writeInt8Array(std::vector* destination, int8_t data[], uint32_t dataSize) { writeInt32(destination, dataSize); for(uint32_t i=0; i < dataSize; i++){ destination->push_back(data[i]); } } void 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(); } *destination = exportedString.value; } void writeInt32Array(std::vector* destination, std::vector data) { writeInt32(destination, data.size()); for(int32_t element: data){ writeInt32(destination, element); } } void writeInt32Array(std::vector* destination, int32_t data[], uint32_t dataSize) { writeInt32(destination, dataSize); for(uint32_t i = 0; i* destination, std::vector data) { writeInt32(destination, data.size()); for(int64_t element: data){ writeInt64(destination, element); } } void writeInt64Array(std::vector* destination, int64_t data[], uint32_t dataSize) { writeInt32(destination, dataSize); for(uint32_t i = 0; i Tag::Tag(uint8_t tagType, tiny_utf8::string name, uint16_t nameSize, T content, uint32_t size) : tagType(tagType), name(name), nameSize(nameSize), content(content) ,size(size) {} End::End() : Tag::Tag(0, "", 0, 0, 0) {} Byte::Byte(tiny_utf8::string name, uint16_t nameSize, int8_t content) : Tag::Tag(1, name, nameSize, content, 1) {} Byte::Byte(uint8_t data[]){ if(validate(data)){ this->tagType = 1; uint8_t nameSizeSlice[] = {data[1], data[2]}; ErrorOr readIntResult = helper::readInt16(nameSizeSlice, 2, 0); if(!readIntResult.isError){ this->nameSize = readIntResult.value; }else{ throw readIntResult.errorCode; } uint8_t nameSlice[this->nameSize+2]; for(int i=0; inameSize+2; i++){ nameSlice[i] = data[i+1]; } ErrorOr readStringResult = helper::readString(nameSlice, this->nameSize, 0); if(!readStringResult.isError){ this->name = readStringResult.value; }else{ throw readStringResult.errorCode; } //int8 needs only one byte this->content = data[this->nameSize+4]; } } //more conditions will be added bool Byte::validate(uint8_t data[]){ if(data[0] == 0x01){ return true; }else{ return false; } } bool validateRawNBTData(uint8_t data[], uint64_t dataSize){ //state machine? //TODO: implement return false; } }