diff --git a/src/lib/nbt.cpp b/src/lib/nbt.cpp index 41c1e02..1d53efa 100644 --- a/src/lib/nbt.cpp +++ b/src/lib/nbt.cpp @@ -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 nextTagTotalSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition) { + uint8_t nextTag; + if (dataSize <= currentPosition) { + return ErrorOr(true, ErrorCodes::OVERRUN); + } else { + nextTag = data[currentPosition]; + } + // deal with compound tags separately + if (nextTag == TagType::COMPOUND || nextTag == TagType::LIST) return ErrorOr(false, ErrorCodes::NOT_YET_KNOWN); + // deal with end tag before trying to access the name + if (nextTag == TagType::END) return ErrorOr(1); + // get name size + ErrorOr nameSize = helper::readInt16(data, dataSize, currentPosition+1); + if (nameSize.isError) { + return ErrorOr(true, nameSize.errorCode); + } + switch (nextTag) { + case TagType::INT8: + // type byte + name size + data byte -> 4 bytes + return ErrorOr((uint64_t) nameSize.value+4); + case TagType::INT16: + // type byte + name size + 2 data bytes -> 5 bytes + return ErrorOr((uint64_t) nameSize.value+5); + case TagType::INT32: + // type byte + name size + 4 data bytes -> 7 bytes + return ErrorOr((uint64_t) nameSize.value+7); + case TagType::INT64: + // type byte + name size + 8 data bytes -> 11 bytes + return ErrorOr((uint64_t) nameSize.value+11); + case TagType::FLOAT: + // type byte + name size + 4 data bytes -> 7 bytes + return ErrorOr((uint64_t) nameSize.value+7); + case TagType::DOUBLE: + // type byte + name size + 8 data bytes -> 11 bytes + return ErrorOr((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 arraySize = helper::readInt32(data, dataSize, currentPosition+totalSize); + if (arraySize.isError) { + return ErrorOr(true, arraySize.errorCode); + } + totalSize += (uint64_t) arraySize.value; + + return ErrorOr(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 stringSize = helper::readInt16(data, dataSize, currentPosition+totalSize); + if (stringSize.isError) { + return ErrorOr(true, stringSize.errorCode); + } + totalSize += (uint64_t) stringSize.value; + + return ErrorOr(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 arraySize = helper::readInt16(data, dataSize, currentPosition+totalSize); + if (arraySize.isError) { + return ErrorOr(true, arraySize.errorCode); + } + totalSize += (uint64_t) arraySize.value*4; + + return ErrorOr(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 arraySize = helper::readInt16(data, dataSize, currentPosition+totalSize); + if (arraySize.isError) { + return ErrorOr(true, arraySize.errorCode); + } + totalSize += (uint64_t) arraySize.value*8; + + return ErrorOr(totalSize); + } + // unknown tag or parsing error + default: + return ErrorOr(true, ErrorCodes::UNKNOWN); + } + } + + ErrorOr nextTagDataSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition){ + + uint8_t nextTag; + if (dataSize <= currentPosition) { + return ErrorOr(true, ErrorCodes::OVERRUN); + } else { + nextTag = data[currentPosition]; + } + + // deal with compound tags separately + if (nextTag == TagType::COMPOUND) return ErrorOr(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(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 nextTagTotalSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition) { - uint8_t nextTag; - if (dataSize <= currentPosition) { - return ErrorOr(true, ErrorCodes::OVERRUN); - } else { - nextTag = data[currentPosition]; - } - // deal with compound tags separately - if (nextTag == TagType::COMPOUND) return ErrorOr(false, ErrorCodes::NOT_YET_KNOWN); - // deal with end tag before trying to access the name - if (nextTag == TagType::END) return ErrorOr(1); - // get name size - ErrorOr nameSize = helper::readInt16(data, dataSize, currentPosition+1); - if (nameSize.isError) { - return ErrorOr(true, nameSize.errorCode); - } - switch (nextTag) { - case TagType::INT8: - // type byte + name size + data byte -> 4 bytes - return ErrorOr((uint64_t) nameSize.value+4); - case TagType::INT16: - // type byte + name size + 2 data bytes -> 5 bytes - return ErrorOr((uint64_t) nameSize.value+5); - case TagType::INT32: - // type byte + name size + 4 data bytes -> 7 bytes - return ErrorOr((uint64_t) nameSize.value+7); - case TagType::INT64: - // type byte + name size + 8 data bytes -> 11 bytes - return ErrorOr((uint64_t) nameSize.value+11); - case TagType::FLOAT: - // type byte + name size + 4 data bytes -> 7 bytes - return ErrorOr((uint64_t) nameSize.value+7); - case TagType::DOUBLE: - // type byte + name size + 8 data bytes -> 11 bytes - return ErrorOr((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 arraySize = helper::readInt32(data, dataSize, currentPosition+totalSize); - if (arraySize.isError) { - return ErrorOr(true, arraySize.errorCode); - } - totalSize += (uint64_t) arraySize.value; - - return ErrorOr(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 stringSize = helper::readInt16(data, dataSize, currentPosition+totalSize); - if (stringSize.isError) { - return ErrorOr(true, stringSize.errorCode); - } - totalSize += (uint64_t) stringSize.value; - - return ErrorOr(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 containedType = nextTagType(data, dataSize, currentPosition+totalSize-1); - if (containedType.isError) { - return ErrorOr(true, containedType.errorCode); - } - ErrorOr listSize = helper::readInt16(data, dataSize, currentPosition+totalSize); - if (listSize.isError) { - return ErrorOr(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(true, ErrorCodes::UNKNOWN); - } - totalSize += listSize.value*factor; - return ErrorOr(totalSize); - } else { - if (containedType.value == TagType::COMPOUND || containedType.value == TagType::LIST) return ErrorOr(false, ErrorCodes::NOT_YET_KNOWN); - //TODO: INT8_ARRAY, STRING, INT32_ARRAY, INT64_ARRAY - } - - return ErrorOr(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 arraySize = helper::readInt16(data, dataSize, currentPosition+totalSize); - if (arraySize.isError) { - return ErrorOr(true, arraySize.errorCode); - } - totalSize += (uint64_t) arraySize.value*4; - - return ErrorOr(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 arraySize = helper::readInt16(data, dataSize, currentPosition+totalSize); - if (arraySize.isError) { - return ErrorOr(true, arraySize.errorCode); - } - totalSize += (uint64_t) arraySize.value*8; - - return ErrorOr(totalSize); - } - // fall-through in case of unknown tag or parsing error - default: - return ErrorOr(true, ErrorCodes::UNKNOWN); - } - } - - ErrorOr nextTagDataSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition){ - - uint8_t nextTag; - if (dataSize <= currentPosition) { - return ErrorOr(true, ErrorCodes::OVERRUN); - } else { - nextTag = data[currentPosition]; - } - - // deal with compound tags separately - if (nextTag == TagType::COMPOUND) return ErrorOr(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(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 diff --git a/src/lib/nbt.hpp b/src/lib/nbt.hpp index 899c9c2..578a1f2 100644 --- a/src/lib/nbt.hpp +++ b/src/lib/nbt.hpp @@ -66,6 +66,9 @@ namespace NBT { void writeInt32Array(std::vector* destination, int32_t data[], uint32_t dataSize); void writeInt64Array(std::vector* destination, std::vector data); void writeInt64Array(std::vector* destination, int64_t data[], uint32_t dataSize); + + ErrorOr nextTagTotalSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition); + ErrorOr nextTagDataSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition); } namespace TagType { @@ -110,8 +113,5 @@ namespace NBT { bool validate(uint8_t data[]); }; - ErrorOr nextTagType(uint8_t data[], uint64_t dataSize, uint64_t currentPosition); - ErrorOr nextTagTotalSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition); - ErrorOr nextTagDataSize(uint8_t data[], uint64_t dataSize, uint64_t currentPosition); bool validateRawNBTData(uint8_t data[], int length, uint64_t initialPosition=0); }