Compare commits

..

No commits in common. "d402d4e0572c8e6ec633c501fa31b0d11ced6255" and "47f39362f46666d50a2b339b001f7751eb011e38" have entirely different histories.

6 changed files with 170 additions and 173 deletions

View File

@ -44,4 +44,6 @@ if [ -f "$PROJECT_BASE_DIR/.localenv.bashrc" ]; then
source "$PROJECT_BASE_DIR/.localenv.bashrc"
fi
unset PROJECT_BASE_DIR
echo "done."

View File

@ -370,132 +370,6 @@ namespace NBT {
writeInt64(destination, data[i]);
}
}
//FIXME: instead of blindly passing the error code upwards, choose
// one that is applicable to the situation (for example replace
// OUT_OF_RANGE with OVERRUN where appropriate)
//
// Does not work for compound tags and lists. This is an intended
// feature as compound tags and lists need to be dealt with
// separately to avoid unnecessarily complex code.
//
// Regardinng lists specifically: The size of some lists can can
// be determined easily by looking at the contained data type and
// size information but cases like string lists or compound lists
// are significantly more difficult to deal with. Parsing their
// contents requires special attention anyway due the tag headers
// of contained tags being absent so they may as well get their
// own function for this as well.
ErrorOr<uint64_t> nextTagTotalSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition) {
uint8_t nextTag;
if (dataSize <= currentPosition) {
return ErrorOr<uint64_t>(true, ErrorCodes::OVERRUN);
} else {
nextTag = data[currentPosition];
}
// deal with compound tags separately
if (nextTag == TagType::COMPOUND || nextTag == TagType::LIST) return ErrorOr<uint64_t>(false, ErrorCodes::NOT_YET_KNOWN);
// deal with end tag before trying to access the name
if (nextTag == TagType::END) return ErrorOr<uint64_t>(1);
// get name size
ErrorOr<int16_t> nameSize = helper::readInt16(data, dataSize, currentPosition+1);
if (nameSize.isError) {
return ErrorOr<uint64_t>(true, nameSize.errorCode);
}
switch (nextTag) {
case TagType::INT8:
// type byte + name size + data byte -> 4 bytes
return ErrorOr<uint64_t>((uint64_t) nameSize.value+4);
case TagType::INT16:
// type byte + name size + 2 data bytes -> 5 bytes
return ErrorOr<uint64_t>((uint64_t) nameSize.value+5);
case TagType::INT32:
// type byte + name size + 4 data bytes -> 7 bytes
return ErrorOr<uint64_t>((uint64_t) nameSize.value+7);
case TagType::INT64:
// type byte + name size + 8 data bytes -> 11 bytes
return ErrorOr<uint64_t>((uint64_t) nameSize.value+11);
case TagType::FLOAT:
// type byte + name size + 4 data bytes -> 7 bytes
return ErrorOr<uint64_t>((uint64_t) nameSize.value+7);
case TagType::DOUBLE:
// type byte + name size + 8 data bytes -> 11 bytes
return ErrorOr<uint64_t>((uint64_t) nameSize.value+11);
case TagType::INT8_ARRAY: {
// type byte + name size + 4 size bytes -> 7 bytes
uint64_t totalSize = (uint64_t) nameSize.value+7;
// add size of actual data (1 byte per entry)
ErrorOr<int32_t> arraySize = helper::readInt32(data, dataSize, currentPosition+totalSize);
if (arraySize.isError) {
return ErrorOr<uint64_t>(true, arraySize.errorCode);
}
totalSize += (uint64_t) arraySize.value;
return ErrorOr<uint64_t>(totalSize);
}
case TagType::STRING: {
// type byte + name size + 2 size bytes -> 5 bytes
uint64_t totalSize = (uint64_t) nameSize.value+5;
// add size of actual data
ErrorOr<int16_t> stringSize = helper::readInt16(data, dataSize, currentPosition+totalSize);
if (stringSize.isError) {
return ErrorOr<uint64_t>(true, stringSize.errorCode);
}
totalSize += (uint64_t) stringSize.value;
return ErrorOr<uint64_t>(totalSize);
}
case TagType::INT32_ARRAY: {
// type byte + name size + 4 size bytes -> 7 bytes
uint64_t totalSize = (uint64_t) nameSize.value+7;
// add size of actual data (4 bytes per entry)
ErrorOr<int16_t> arraySize = helper::readInt16(data, dataSize, currentPosition+totalSize);
if (arraySize.isError) {
return ErrorOr<uint64_t>(true, arraySize.errorCode);
}
totalSize += (uint64_t) arraySize.value*4;
return ErrorOr<uint64_t>(totalSize);
}
case TagType::INT64_ARRAY: {
// type byte + name size + 4 size bytes -> 7 bytes
uint64_t totalSize = (uint64_t) nameSize.value+7;
// add size of actual data (8 bytes per entry)
ErrorOr<int16_t> arraySize = helper::readInt16(data, dataSize, currentPosition+totalSize);
if (arraySize.isError) {
return ErrorOr<uint64_t>(true, arraySize.errorCode);
}
totalSize += (uint64_t) arraySize.value*8;
return ErrorOr<uint64_t>(totalSize);
}
// unknown tag or parsing error
default:
return ErrorOr<uint64_t>(true, ErrorCodes::UNKNOWN);
}
}
ErrorOr<uint32_t> nextTagDataSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition){
uint8_t nextTag;
if (dataSize <= currentPosition) {
return ErrorOr<uint32_t>(true, ErrorCodes::OVERRUN);
} else {
nextTag = data[currentPosition];
}
// deal with compound tags separately
if (nextTag == TagType::COMPOUND) return ErrorOr<uint32_t>(true, ErrorCodes::NOT_YET_KNOWN);
// deal with end tag before trying to access the name
if (nextTag == TagType::END) return 0;
//TODO: implement for all the remaining types
// unknown tag or parsing error
return ErrorOr<uint32_t>(true, ErrorCodes::UNKNOWN);
}
}
//Tag constructors
@ -550,6 +424,161 @@ namespace NBT {
}
}
ErrorOr<uint8_t> nextTagType(uint8_t data[], uint64_t dataSize, uint64_t currentPosition) {
if (dataSize <= currentPosition) {
return ErrorOr<uint8_t>(true, ErrorCodes::OVERRUN);
} else {
return ErrorOr<uint8_t>(data[currentPosition]);
}
}
//FIXME: instead of blindly passing the error code upwards, choose one that
// is applicable to the situation (for example replace OUT_OF_RANGE with
// OVERRUN where appropriate)
ErrorOr<uint64_t> nextTagTotalSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition) {
ErrorOr<uint8_t> nextTag = nextTagType(data, dataSize, currentPosition);
if (nextTag.isError) {
return ErrorOr<uint64_t>(true, nextTag.errorCode);
}
// deal with compound tags separately
if (nextTag.value == TagType::COMPOUND) return ErrorOr<uint64_t>(false, ErrorCodes::NOT_YET_KNOWN);
// deal with end tag before trying to access the name
if (nextTag.value == TagType::END) return ErrorOr<uint64_t>(1);
// get name size
ErrorOr<uint16_t> nameSize = (uint16_t) helper::readInt16(data, dataSize, currentPosition+1);
if (nameSize.isError) {
return ErrorOr<uint64_t>(true, nameSize.errorCode);
}
switch (nextTag.value) {
case TagType::INT8:
// type byte + name size + data byte -> 4 bytes
return ErrorOr<uint64_t>((uint64_t) nameSize.value+4);
case TagType::INT16:
// type byte + name size + 2 data bytes -> 5 bytes
return ErrorOr<uint64_t>((uint64_t) nameSize.value+5);
case TagType::INT32:
// type byte + name size + 4 data bytes -> 7 bytes
return ErrorOr<uint64_t>((uint64_t) nameSize.value+7);
case TagType::INT64:
// type byte + name size + 8 data bytes -> 11 bytes
return ErrorOr<uint64_t>((uint64_t) nameSize.value+11);
case TagType::FLOAT:
// type byte + name size + 4 data bytes -> 7 bytes
return ErrorOr<uint64_t>((uint64_t) nameSize.value+7);
case TagType::DOUBLE:
// type byte + name size + 8 data bytes -> 11 bytes
return ErrorOr<uint64_t>((uint64_t) nameSize.value+11);
case TagType::INT8_ARRAY:
// type byte + name size + 4 size bytes -> 7 bytes
uint64_t totalSize = (uint64_t) nameSize.value+7;
// add size of actual data (1 byte per entry)
ErrorOr<int32_t> arraySize = helper::readInt32(data, dataSize, currentPosition+totalSize);
if (arraySize.isError) {
return ErrorOr<uint64_t>(true, arraySize.errorCode);
}
totalSize += (uint64_t) arraySize.value;
return ErrorOr<uint64_t>(totalSize);
case TagType::STRING:
// type byte + name size + 2 size bytes -> 5 bytes
uint64_t totalSize = (uint64_t) nameSize.value+5;
// add size of actual data
ErrorOr<int16_t> stringSize = helper::readInt16(data, dataSize, currentPosition+totalSize);
if (stringSize.isError) {
return ErrorOr<uint64_t>(true, stringSize.errorCode);
}
totalSize += (uint64_t) stringSize.value;
return ErrorOr<uint64_t>(totalSize);
case TagType::LIST:
// type byte + name size + type prefix + 4 size bytes -> 8 bytes
uint64_t totalSize = (uint64_t) nameSize.value+8;
// determine size of actual data
ErrorOr<uint8_t> containedType = nextTagType(data, dataSize, currentPosition+totalSize-1);
if (containedType.isError) {
return ErrorOr<uint64_t>(true, containedType.errorCode);
}
ErrorOr<int16_t> listSize = helper::readInt16(data, dataSize, currentPosition+totalSize);
if (listSize.isError) {
return ErrorOr<uint64_t>(true, listSize.errorCode);
}
// Can we just multiply list size with data type size?
if (containedType.value == TagType::END || containedType.value == TagType::INT8 || containedType.value == TagType::INT16 || containedType.value == TagType::INT32 || containedType.value == TagType::INT64 || containedType.value == TagType::FLOAT || containedType.value == TagType::DOUBLE) {
uint8_t factor;
switch (containedType.value) {
case TagType::END:
factor = 1;
case TagType::INT8:
factor = 1;
case TagType::INT16:
factor = 2;
case TagType::INT32:
factor = 4;
case TagType::INT64:
factor = 8;
case TagType::FLOAT:
factor = 4;
case TagType::DOUBLE:
factor = 8;
default:
// How would you even get here?
return ErrorOr<uint64_t>(true, ErrorCodes::UNKNOWN);
}
totalSize += listSize*factor;
return ErrorOr<uint64_t>(totalSize);
} else {
if (containedType.value == TagType::COMPOUND || containedType.value == TagType::LIST) return ErrorOr<uint64_t>(false, ErrorCodes::NOT_YET_KNOWN);
//TODO: INT8_ARRAY, STRING, INT32_ARRAY, INT64_ARRAY
}
return ErrorOr<uint64_t>(true, ErrorCodes::UNKNOWN);
case TagType::INT32_ARRAY:
// type byte + name size + 4 size bytes -> 7 bytes
uint64_t totalSize = (uint64_t) nameSize.value+7;
// add size of actual data (4 bytes per entry)
ErrorOr<int16_t> arraySize = helper::readInt16(data, dataSize, currentPosition+totalSize);
if (arraySize.isError) {
return ErrorOr<uint64_t>(true, arraySize.errorCode);
}
totalSize += (uint64_t) arraySize.value*4;
return ErrorOr<uint64_t>(totalSize);
case TagType::INT64_ARRAY:
// type byte + name size + 4 size bytes -> 7 bytes
uint64_t totalSize = (uint64_t) nameSize.value+7;
// add size of actual data (8 bytes per entry)
ErrorOr<int16_t> arraySize = helper::readInt16(data, dataSize, currentPosition+totalSize);
if (arraySize.isError) {
return ErrorOr<uint64_t>(true, arraySize.errorCode);
}
totalSize += (uint64_t) arraySize.value*8;
return ErrorOr<uint64_t>(totalSize);
// fall-through in case of unknown tag or parsing error
default:
return ErrorOr<uint64_t>(true, ErrorCodes::UNKNOWN);
}
}
ErrorOr<uint32_t> nextTagDataSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition){
ErrorOr<uint8_t> nextTag = nexttagType(data, dataSize, currentPosition);
if (nextTag.isError) {
return ErrorOr<int64_t>(true, nextTag.errorCode);
}
// deal with compound tags separately
if (nextTag.value == TagType::COMPOUND) return ErrorOr<uint64_t>(true, ErrorCodes::NOT_YET_KNOWN);
// deal with end tag before trying to access the name
if (nextTag.value == TagType::END) return 0;
//TODO: implement for all the remaining types
// fall-through in case of unknown tag or parsing error
return ErrorOr<uint32_t>(true, ErrorCodes::UNKNOWN);
}
bool validateRawNBTData(uint8_t data[], uint64_t dataSize, uint64_t initialPosition){
//TODO: find out the size of the next tag
//TODO: consume tag

View File

@ -66,9 +66,6 @@ namespace NBT {
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);
void writeInt64Array(std::vector<uint8_t>* destination, int64_t data[], uint32_t dataSize);
ErrorOr<uint64_t> nextTagTotalSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<uint32_t> nextTagDataSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
}
namespace TagType {
@ -113,5 +110,8 @@ namespace NBT {
bool validate(uint8_t data[]);
};
ErrorOr<uint8_t> nextTagType(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<uint64_t> nextTagTotalSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<uint32_t> nextTagDataSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
bool validateRawNBTData(uint8_t data[], int length, uint64_t initialPosition=0);
}

View File

@ -524,5 +524,15 @@ int main(){
std::cout << "Passed writeString NBT helper test." << std::endl;
//Byte tag constructor test
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"));
std::cout << "Passed Byte Tag constructor test." << std::endl;
return 0;
}

View File

@ -1,44 +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 <iostream>
#include <cstdint>
#include <vector>
#include <fstream>
#include "assert.hpp"
#include "../lib/nbt.hpp"
#include "../lib/error.hpp"
#include "../lib/javacompat.hpp"
int main(){
std::cout << "################################################################################" << std::endl;
std::cout << "NBT object tests" << std::endl;
std::cout << "################################################################################" << std::endl;
//Byte tag constructor test
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"));
std::cout << "Passed Byte Tag constructor test." << std::endl;
return 0;
}