Compare commits
5 Commits
47f39362f4
...
d402d4e057
Author | SHA1 | Date |
---|---|---|
BodgeMaster | d402d4e057 | |
BodgeMaster | f2ae84c062 | |
BodgeMaster | 1ee8d47e2c | |
BodgeMaster | 3741d844f1 | |
BodgeMaster | 72dffde7c9 |
|
@ -44,6 +44,4 @@ if [ -f "$PROJECT_BASE_DIR/.localenv.bashrc" ]; then
|
|||
source "$PROJECT_BASE_DIR/.localenv.bashrc"
|
||||
fi
|
||||
|
||||
unset PROJECT_BASE_DIR
|
||||
|
||||
echo "done."
|
||||
|
|
281
src/lib/nbt.cpp
281
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<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,161 +550,6 @@ 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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -524,15 +524,5 @@ 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;
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
// 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;
|
||||
}
|
Loading…
Reference in New Issue