// 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 "nbt.h++" #include "error.h++" #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::RANGE_ERROR); return ErrorOr((int8_t) data[currentPosition]); } ErrorOr readInt16(uint8_t data[], uint64_t dataSize, uint64_t currentPosition) { if (dataSize(true, ErrorCodes::RANGE_ERROR); 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::RANGE_ERROR); 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::RANGE_ERROR); 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: we just assume that float is a single-precision IEEE754 // floating point number, also we are using endian-dependent // implementations ErrorOr readFloat32(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::RANGE_ERROR); if (dataSize(true, ErrorCodes::OVERRUN_ERROR); #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::readFloat32: An implementation for your endianness is unavailable." #endif #endif float dereferencedValue = *value; delete value; return ErrorOr(dereferencedValue); } //FIXME: we just assume that double is a double-precision IEEE754 // floating point number, also we are using endian-dependent // implementations ErrorOr readFloat64(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::RANGE_ERROR); if (dataSize(true, ErrorCodes::OVERRUN_ERROR); #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::readFloat64: 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_ERROR); std::vector result = std::vector(); for (int i=0; i>(result); } // Maybe use a struct that holds decoded (de-Java-fied) string // data, decoded size, and original size? Original size is needed // so the parser knows where to continue. //ErrorOr<> readString(uint8_t data[], uint64_t dataSize, uint64_t currentPosition) { //TODO: implement // return ErrorOr<>(""); //} 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_ERROR); 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_ERROR); 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: we just assume that float is a single-precision IEEE754 // floating point number, also endian specific implementation void writeFloat32(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: we just assume that double is a single-precision IEEE754 // floating point number, also endian specific implementation void writeFloat64(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) { } void writeInt8Array(std::vector* destination, int8_t data[], uint64_t dataSize) { } //void writeString(std::vector* destination, data) { //} void writeInt32Array(std::vector* destination, std::vector data) { } void writeInt32Array(std::vector* destination, int32_t data[], uint64_t dataSize) { } void writeInt64Array(std::vector* destination, std::vector data) { } void writeInt64Array(std::vector* destination, int64_t data[], uint64_t dataSize) { } } bool validateRawNBTData(uint8_t data[], uint64_t dataSize){ //state machine? //TODO: implement return false; } }