Compare commits

...

4 Commits

Author SHA1 Message Date
BodgeMaster 53878c3e2b lib/nbt: Start implementing the in-memory data structure for NBT 2022-09-15 06:06:47 +02:00
BodgeMaster ad291ee77d lib/nbt: Capitalize NBT::Helper because I feel like it 2022-09-15 02:00:07 +02:00
BodgeMaster 6149418f52 Add a level.dat to test the NBT parser on. 2022-09-15 01:51:51 +02:00
BodgeMaster ac12bcf865 Makefile: Finally get rid of this as we don't need it.
A makefile may be added back when we are preparing distribution but
it’s a long road until then.
2022-09-15 01:06:59 +02:00
6 changed files with 64 additions and 126 deletions

View File

@ -1,12 +0,0 @@
all: build
build:
bash ./scripts/build.sh
clean:
bash ./scripts/clean.sh
mrproper:
bash ./scripts/clean.sh
bash ./scripts/clean_dependencies.sh
setup:
bash ./scripts/setup_project.sh
test:
bash ./scripts/test.sh

Binary file not shown.

Binary file not shown.

View File

@ -89,6 +89,8 @@ namespace ErrorCodes {
const uint8_t NOT_YET_KNOWN = 7;
const uint8_t INVALID_TYPE = 8;
const uint8_t UNIMPLEMENTED = 254;
const uint8_t UNKNOWN = 255;

View File

@ -36,7 +36,7 @@
#endif
namespace NBT {
namespace helper {
namespace Helper {
ErrorOr<int8_t> readInt8(uint8_t data[], uint64_t dataSize, uint64_t currentPosition) {
if (currentPosition>=dataSize) return ErrorOr<int8_t>(true, ErrorCodes::OUT_OF_RANGE);
return ErrorOr<int8_t>((int8_t) data[currentPosition]);
@ -92,7 +92,7 @@ namespace NBT {
*(valueAsBytes+2) = data[currentPosition+1];
*(valueAsBytes+3) = data[currentPosition];
#else
#error "NBT::helper::readFloat: An implementation for your endianness is unavailable."
#error "NBT::Helper::readFloat: An implementation for your endianness is unavailable."
#endif
#endif
float dereferencedValue = *value;
@ -126,7 +126,7 @@ namespace NBT {
*(valueAsBytes+6) = data[currentPosition+1];
*(valueAsBytes+7) = data[currentPosition];
#else
#error "NBT::helper::readDouble: An implementation for your endianness is unavailable."
#error "NBT::Helper::readDouble: An implementation for your endianness is unavailable."
#endif
#endif
double dereferencedValue = *value;
@ -232,7 +232,7 @@ namespace NBT {
destination->push_back(*(valueAsBytes+1));
destination->push_back(*valueAsBytes);
#else
#error "NBT::helper::writeInt16: An implementation for your endianness is unavailable."
#error "NBT::Helper::writeInt16: An implementation for your endianness is unavailable."
#endif
#endif
delete value;
@ -255,7 +255,7 @@ namespace NBT {
destination->push_back(*(valueAsBytes+1));
destination->push_back(*valueAsBytes);
#else
#error "NBT::helper::writeInt16: An implementation for your endianness is unavailable."
#error "NBT::Helper::writeInt16: An implementation for your endianness is unavailable."
#endif
#endif
delete value;
@ -286,7 +286,7 @@ namespace NBT {
destination->push_back(*(valueAsBytes+1));
destination->push_back(*valueAsBytes);
#else
#error "NBT::helper::writeInt16: An implementation for your endianness is unavailable."
#error "NBT::Helper::writeInt16: An implementation for your endianness is unavailable."
#endif
#endif
delete value;
@ -309,7 +309,7 @@ namespace NBT {
destination->push_back(*(valueAsBytes+1));
destination->push_back(*valueAsBytes);
#else
#error "NBT::helper::writeInt16: An implementation for your endianness is unavailable."
#error "NBT::Helper::writeInt16: An implementation for your endianness is unavailable."
#endif
#endif
delete value;
@ -340,7 +340,7 @@ namespace NBT {
destination->push_back(*(valueAsBytes+1));
destination->push_back(*valueAsBytes);
#else
#error "NBT::helper::writeInt16: An implementation for your endianness is unavailable."
#error "NBT::Helper::writeInt16: An implementation for your endianness is unavailable."
#endif
#endif
delete value;
@ -363,7 +363,7 @@ namespace NBT {
void writeString(std::vector<uint8_t>* destination, tiny_utf8::string data) {
ErrorOr<std::vector<uint8_t>> exportedString = JavaCompat::exportJavaString(data);
if(exportedString.isError){
std::cerr << "NBT::helpers::writeString encountered an error: " << (int) exportedString.errorCode << std::endl;
std::cerr << "NBT::Helpers::writeString encountered an error: " << (int) exportedString.errorCode << std::endl;
std::abort();
}
*destination = exportedString.value;
@ -426,7 +426,7 @@ namespace NBT {
// deal with end tag before trying to access the name
if (nextTag == TagType::END) return ErrorOr<uint64_t>(1);
ErrorOr<int16_t> nameSize = helper::readInt16(data, dataSize, currentPosition+1);
ErrorOr<int16_t> nameSize = Helper::readInt16(data, dataSize, currentPosition+1);
if (nameSize.isError) {
return ErrorOr<uint64_t>(true, nameSize.errorCode);
}
@ -446,28 +446,28 @@ namespace NBT {
case TagType::DOUBLE:
return ErrorOr<uint64_t>(prefixSize+8);
case TagType::INT8_ARRAY: {
ErrorOr<int32_t> arrayLength = helper::readInt32(data, dataSize, currentPosition+prefixSize);
ErrorOr<int32_t> arrayLength = Helper::readInt32(data, dataSize, currentPosition+prefixSize);
if (arrayLength.isError) {
return ErrorOr<uint64_t>(true, arrayLength.errorCode);
}
return ErrorOr<uint64_t>((uint64_t) arrayLength.value + prefixSize + 4);
}
case TagType::STRING: {
ErrorOr<int16_t> stringSize = helper::readInt16(data, dataSize, currentPosition+prefixSize);
ErrorOr<int16_t> stringSize = Helper::readInt16(data, dataSize, currentPosition+prefixSize);
if (stringSize.isError) {
return ErrorOr<uint64_t>(true, stringSize.errorCode);
}
return ErrorOr<uint64_t>((uint64_t) stringSize.value + prefixSize + 2);
}
case TagType::INT32_ARRAY: {
ErrorOr<int32_t> arrayLength = helper::readInt32(data, dataSize, currentPosition+prefixSize);
ErrorOr<int32_t> arrayLength = Helper::readInt32(data, dataSize, currentPosition+prefixSize);
if (arrayLength.isError) {
return ErrorOr<uint64_t>(true, arrayLength.errorCode);
}
return ErrorOr<uint64_t>((uint64_t) arrayLength.value*4 + prefixSize + 4);
}
case TagType::INT64_ARRAY: {
ErrorOr<int32_t> arrayLength = helper::readInt32(data, dataSize, currentPosition+prefixSize);
ErrorOr<int32_t> arrayLength = Helper::readInt32(data, dataSize, currentPosition+prefixSize);
if (arrayLength.isError) {
return ErrorOr<uint64_t>(true, arrayLength.errorCode);
}
@ -509,7 +509,7 @@ namespace NBT {
return ErrorOr<int32_t>(1);
}
ErrorOr<int16_t> nameSize = helper::readInt16(data, dataSize, currentPosition+1);
ErrorOr<int16_t> nameSize = Helper::readInt16(data, dataSize, currentPosition+1);
if (nameSize.isError) {
return ErrorOr<int32_t>(true, nameSize.errorCode);
}
@ -517,10 +517,10 @@ namespace NBT {
uint64_t prefixSize = (uint64_t) nameSize.value + 3;
switch (nextTag) {
case TagType::INT8_ARRAY: {
return helper::readInt32(data, dataSize, currentPosition+prefixSize);
return Helper::readInt32(data, dataSize, currentPosition+prefixSize);
}
case TagType::STRING: {
ErrorOr<int16_t> stringSize = helper::readInt16(data, dataSize, currentPosition+prefixSize);
ErrorOr<int16_t> stringSize = Helper::readInt16(data, dataSize, currentPosition+prefixSize);
if (stringSize.isError) {
return ErrorOr<int32_t>(true, stringSize.errorCode);
}
@ -528,13 +528,13 @@ namespace NBT {
}
case TagType::LIST: {
// add an additional byte for the contained data type
return helper::readInt32(data, dataSize, currentPosition+prefixSize+1);
return Helper::readInt32(data, dataSize, currentPosition+prefixSize+1);
}
case TagType::INT32_ARRAY: {
return helper::readInt32(data, dataSize, currentPosition+prefixSize);
return Helper::readInt32(data, dataSize, currentPosition+prefixSize);
}
case TagType::INT64_ARRAY: {
return helper::readInt32(data, dataSize, currentPosition+prefixSize);
return Helper::readInt32(data, dataSize, currentPosition+prefixSize);
}
default:
// unknown tag or parsing error
@ -543,56 +543,15 @@ namespace NBT {
}
}
//Tag constructors
// generic class that all tag types are derived from
template <typename T>
Tag<T>::Tag(uint8_t tagType, tiny_utf8::string name, uint16_t nameSize, T content, uint32_t size)
: tagType(tagType), name(name), nameSize(nameSize), content(content) ,size(size)
{}
Tag<T>::Tag() {
End::End() : Tag::Tag(0, "", 0, 0, 0) {}
Byte::Byte(tiny_utf8::string name, uint16_t nameSize, int8_t content)
: Tag::Tag(1, name, nameSize, content, 1)
{}
Byte::Byte(uint8_t data[]){
if(validate(data)){
this->tagType = 1;
uint8_t nameSizeSlice[] = {data[1], data[2]};
ErrorOr<int16_t> readIntResult = helper::readInt16(nameSizeSlice, 2, 0);
if(!readIntResult.isError){
this->nameSize = readIntResult.value;
}else{
throw readIntResult.errorCode;
}
uint8_t nameSlice[this->nameSize+2];
for(int i=0; i<this->nameSize+2; i++){
nameSlice[i] = data[i+1];
}
ErrorOr<tiny_utf8::string> readStringResult = helper::readString(nameSlice, this->nameSize, 0);
if(!readStringResult.isError){
this->name = readStringResult.value;
}else{
throw readStringResult.errorCode;
}
//int8 needs only one byte
this->content = data[this->nameSize+4];
}
}
//more conditions will be added
bool Byte::validate(uint8_t data[]){
if(data[0] == 0x01){
return true;
}else{
return false;
}
template <typename T>
ErrorOr<std::vector<uint8_t>> Tag<T>::toRawData() {
return ErrorOr<std::vector<uint8_t>>(true, ErrorCodes::INVALID_TYPE);
}
bool validateRawListContents(uint8_t data[], uint64_t dataSize, uint64_t initialPosition, uint64_t* processedDataSize) {
@ -601,7 +560,7 @@ namespace NBT {
// headerless tags
//
// add one byte to position to skip the type byte
ErrorOr<int32_t> elementCount = helper::readInt32(data, dataSize, initialPosition+1);
ErrorOr<int32_t> elementCount = Helper::readInt32(data, dataSize, initialPosition+1);
if (elementCount.isError) {
return false;
}
@ -634,7 +593,7 @@ namespace NBT {
}
case TagType::INT8_ARRAY: {
for (int32_t i=0; i<elementCount.value; i++) {
ErrorOr<std::vector<int8_t>> nextArray = helper::readInt8Array(data, dataSize, initialPosition+*processedDataSize);
ErrorOr<std::vector<int8_t>> nextArray = Helper::readInt8Array(data, dataSize, initialPosition+*processedDataSize);
if (nextArray.isError) {
return false;
}
@ -644,12 +603,12 @@ namespace NBT {
}
case TagType::STRING: {
for (int32_t i=0; i<elementCount.value; i++) {
ErrorOr<tiny_utf8::string> nextString = helper::readString(data, dataSize, initialPosition+*processedDataSize);
ErrorOr<tiny_utf8::string> nextString = Helper::readString(data, dataSize, initialPosition+*processedDataSize);
if (nextString.isError) {
return false;
}
// this cannot be an error because it just got checked
int16_t nextStringSize = helper::readInt16(data, dataSize, initialPosition+*processedDataSize).value;
int16_t nextStringSize = Helper::readInt16(data, dataSize, initialPosition+*processedDataSize).value;
*processedDataSize += (uint64_t) nextStringSize + 2;
}
return true;
@ -685,7 +644,7 @@ namespace NBT {
}
case TagType::INT32_ARRAY: {
for (int32_t i=0; i<elementCount.value; i++) {
ErrorOr<std::vector<int32_t>> nextArray = helper::readInt32Array(data, dataSize, initialPosition+*processedDataSize);
ErrorOr<std::vector<int32_t>> nextArray = Helper::readInt32Array(data, dataSize, initialPosition+*processedDataSize);
if (nextArray.isError) {
return false;
}
@ -695,7 +654,7 @@ namespace NBT {
}
case TagType::INT64_ARRAY: {
for (int32_t i=0; i<elementCount.value; i++) {
ErrorOr<std::vector<int64_t>> nextArray = helper::readInt64Array(data, dataSize, initialPosition+*processedDataSize);
ErrorOr<std::vector<int64_t>> nextArray = Helper::readInt64Array(data, dataSize, initialPosition+*processedDataSize);
if (nextArray.isError) {
return false;
}
@ -737,11 +696,11 @@ namespace NBT {
uint64_t currentPosition = initialPosition;
#define return if (processedDataSize!=nullptr) *processedDataSize = currentPosition-initialPosition; return
while (currentPosition<dataSize) {
ErrorOr<uint64_t> nextTagSize = helper::totalTagSize(data, dataSize, currentPosition);
ErrorOr<uint64_t> nextTagSize = Helper::totalTagSize(data, dataSize, currentPosition);
if (nextTagSize.isError) {
if (nextTagSize.errorCode == ErrorCodes::NOT_YET_KNOWN) {
// attempt parsing the name
ErrorOr<tiny_utf8::string> tagName = helper::readString(data, dataSize, currentPosition+1);
ErrorOr<tiny_utf8::string> tagName = Helper::readString(data, dataSize, currentPosition+1);
if (tagName.isError) {
return false;
}
@ -750,7 +709,7 @@ namespace NBT {
//
// there is no way this is an error bc it gets
// checked while trying to parse the string above
int16_t nameSize = helper::readInt16(data, dataSize, currentPosition+1).value;
int16_t nameSize = Helper::readInt16(data, dataSize, currentPosition+1).value;
uint64_t* processedTagSize = new uint64_t;
*processedTagSize = 0;
@ -791,7 +750,7 @@ namespace NBT {
// nameSize cannot be an error here bc it got checked in
// nextTagSize() already
int16_t nameSize = helper::readInt16(data, dataSize, currentPosition+1).value;
int16_t nameSize = Helper::readInt16(data, dataSize, currentPosition+1).value;
// attempt parsing the name
//
@ -800,7 +759,7 @@ namespace NBT {
// being guarded against with
// if (currentPosition + nextTagSize.value > dataSize) return false;
// It might, however, turn out to be a useful check in the future.
ErrorOr<tiny_utf8::string> name = helper::readString(data, dataSize, currentPosition+1);
ErrorOr<tiny_utf8::string> name = Helper::readString(data, dataSize, currentPosition+1);
if (name.isError) {
return false;
}
@ -825,7 +784,7 @@ namespace NBT {
// in the future.
//
// type byte + two name size bytes = 3
ErrorOr<tiny_utf8::string> content = helper::readString(data, dataSize, currentPosition+nameSize+3);
ErrorOr<tiny_utf8::string> content = Helper::readString(data, dataSize, currentPosition+nameSize+3);
if (content.isError) {
return false;
}

View File

@ -41,7 +41,7 @@
#include "error.hpp"
namespace NBT {
namespace helper {
namespace Helper {
ErrorOr<int8_t> readInt8(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<int16_t> readInt16(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
ErrorOr<int32_t> readInt32(uint8_t data[], uint64_t dataSize, uint64_t currentPosition);
@ -72,45 +72,34 @@ namespace NBT {
}
namespace TagType {
const uint8_t END = 0;
const uint8_t INT8 = 1;
const uint8_t INT16 = 2;
const uint8_t INT32 = 3;
const uint8_t INT64 = 4;
const uint8_t FLOAT = 5;
const uint8_t DOUBLE = 6;
const uint8_t INT8_ARRAY = 7;
const uint8_t STRING = 8;
const uint8_t LIST = 9;
const uint8_t COMPOUND = 10;
const uint8_t INT32_ARRAY= 11;
const uint8_t INT64_ARRAY= 12;
const uint8_t END = 0;
const uint8_t INT8 = 1;
const uint8_t INT16 = 2;
const uint8_t INT32 = 3;
const uint8_t INT64 = 4;
const uint8_t FLOAT = 5;
const uint8_t DOUBLE = 6;
const uint8_t INT8_ARRAY = 7;
const uint8_t STRING = 8;
const uint8_t LIST = 9;
const uint8_t COMPOUND = 10;
const uint8_t INT32_ARRAY= 11;
const uint8_t INT64_ARRAY= 12;
// This is a workaround that's not part of the spec.
const uint8_t INVALID = 255;
// This class is used as a placeholder for implementing the end tag.
class End {};
}
//Generic parent class to make declaration easier
// generic class that all tag types are derived from
template <typename T>
class Tag{
public:
uint8_t tagType;
tiny_utf8::string name;
uint16_t nameSize;
T content;
int32_t size;
struct Tag {
const uint8_t type = TagType::INVALID;
T* containedData;
Tag(){}
Tag(uint8_t tagType, tiny_utf8::string name, uint16_t nameSize, T content, uint32_t size);
};
class End: public Tag<uint8_t>{
public:
End();
};
class Byte: public Tag<int8_t>{
public:
Byte(tiny_utf8::string name, uint16_t nameSize, int8_t content);
Byte(uint8_t data[]);
bool validate(uint8_t data[]);
Tag();
ErrorOr<std::vector<uint8_t>> toRawData();
};
bool validateRawNBTData(uint8_t data[], uint64_t dataSize, uint64_t initialPosition=0, uint64_t* processedDataSize=nullptr);