lib/nbt: Move the functions for getting tag sizes into the helper namespace, give up on handling lists the same as all other tags

I tried dealing with lists in the same way as with other more basic tags
but came to the conclusion that this is most likely not feasible in the same
way that it is not feasible for compounds. It would require a mini-parser
that can deal with all sorts of tags (including nested lists and compounds).

Instead, an approach more similar to the recursion for compound tags will
be used (using its own function to deal with the missing tag headers ofc).
BodgeMaster-unfinished
BodgeMaster 2022-08-11 07:43:54 +02:00
parent 396b9673fd
commit f5d85da98c
2 changed files with 129 additions and 169 deletions

View File

@ -370,6 +370,132 @@ 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
@ -424,172 +550,6 @@ namespace NBT {
}
}
//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) {
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) 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::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;
break;
case TagType::INT8:
factor = 1;
break;
case TagType::INT16:
factor = 2;
break;
case TagType::INT32:
factor = 4;
break;
case TagType::INT64:
factor = 8;
break;
case TagType::FLOAT:
factor = 4;
break;
case TagType::DOUBLE:
factor = 8;
break;
default:
// How would you even get here?
return ErrorOr<uint64_t>(true, ErrorCodes::UNKNOWN);
}
totalSize += listSize.value*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){
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
// 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,6 +66,9 @@ 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 {
@ -110,8 +113,5 @@ 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);
}