NBT: rework type system

This commit is contained in:
Matthias Schiffer 2018-07-21 18:07:45 +02:00
parent 315bb38444
commit f1f783877f
Signed by: neocturne
GPG key ID: 16EF3F64CB201D9C
17 changed files with 162 additions and 228 deletions

View file

@ -40,12 +40,16 @@ private:
const uint8_t *value; const uint8_t *value;
public: public:
static const MakeType<ByteArrayTag> Type;
ByteArrayTag(Buffer *buffer) { ByteArrayTag(Buffer *buffer) {
len = buffer->get32(); len = buffer->get32();
value = buffer->get(len); value = buffer->get(len);
} }
virtual Type getType() const {
return Type::ByteArray; virtual const TagType & getType() const {
return Type;
} }
virtual void print(std::ostream& os, const std::string &indent) const { virtual void print(std::ostream& os, const std::string &indent) const {

View file

@ -37,12 +37,15 @@ private:
uint8_t value; uint8_t value;
public: public:
static const MakeType<ByteTag> Type;
ByteTag(Buffer *buffer) { ByteTag(Buffer *buffer) {
value = buffer->get8(); value = buffer->get8();
} }
virtual Type getType() const { virtual const TagType & getType() const {
return Type::Byte; return Type;
} }
virtual void print(std::ostream& os, const std::string &) const { virtual void print(std::ostream& os, const std::string &) const {

View file

@ -26,6 +26,7 @@
#pragma once #pragma once
#include "EndTag.hpp"
#include "Tag.hpp" #include "Tag.hpp"
#include <string> #include <string>
@ -37,18 +38,21 @@ namespace NBT {
class CompoundTag : public Tag, public std::unordered_map<std::string, std::shared_ptr<const Tag>> { class CompoundTag : public Tag, public std::unordered_map<std::string, std::shared_ptr<const Tag>> {
public: public:
static const MakeType<CompoundTag> Type;
CompoundTag(Buffer *buffer) { CompoundTag(Buffer *buffer) {
while (true) { while (true) {
std::pair<std::string, std::shared_ptr<const Tag>> v = Tag::readNamedTag(buffer); std::pair<std::string, std::shared_ptr<const Tag>> v = Tag::readNamedTag(buffer);
if (v.second->getType() == Type::End) if (v.second->getType() == EndTag::Type)
break; break;
insert(std::move(v)); insert(std::move(v));
} }
} }
virtual Type getType() const { virtual const TagType & getType() const {
return Type::Compound; return Type;
} }
virtual void print(std::ostream& os, const std::string &indent) const { virtual void print(std::ostream& os, const std::string &indent) const {

View file

@ -37,12 +37,15 @@ private:
const uint8_t *ptr; const uint8_t *ptr;
public: public:
static const MakeType<DoubleTag> Type;
DoubleTag(Buffer *buffer) { DoubleTag(Buffer *buffer) {
ptr = buffer->get(8); ptr = buffer->get(8);
} }
virtual Type getType() const { virtual const TagType & getType() const {
return Type::Double; return Type;
} }
virtual void print(std::ostream& os, const std::string &) const { virtual void print(std::ostream& os, const std::string &) const {

View file

@ -34,10 +34,13 @@ namespace NBT {
class EndTag : public Tag { class EndTag : public Tag {
public: public:
EndTag() {} static const MakeType<EndTag> Type;
virtual Type getType() const {
return Type::End; EndTag(Buffer *) {}
virtual const TagType & getType() const {
return Type;
} }
virtual void print(std::ostream&, const std::string &) const { virtual void print(std::ostream&, const std::string &) const {

View file

@ -36,12 +36,15 @@ class FloatTag : public Tag {
const uint8_t *ptr; const uint8_t *ptr;
public: public:
static const MakeType<FloatTag> Type;
FloatTag(Buffer *buffer) { FloatTag(Buffer *buffer) {
ptr = buffer->get(4); ptr = buffer->get(4);
} }
virtual Type getType() const { virtual const TagType & getType() const {
return Type::Float; return Type;
} }
virtual void print(std::ostream& os, const std::string &) const { virtual void print(std::ostream& os, const std::string &) const {

View file

@ -40,13 +40,16 @@ private:
const uint8_t *ptr; const uint8_t *ptr;
public: public:
static const MakeType<IntArrayTag> Type;
IntArrayTag(Buffer *buffer) { IntArrayTag(Buffer *buffer) {
len = buffer->get32(); len = buffer->get32();
ptr = buffer->get(4*len); ptr = buffer->get(4*len);
} }
virtual Type getType() const { virtual const TagType & getType() const {
return Type::IntArray; return Type;
} }
virtual void print(std::ostream& os, const std::string &indent) const { virtual void print(std::ostream& os, const std::string &indent) const {

View file

@ -37,12 +37,15 @@ private:
const uint8_t *ptr; const uint8_t *ptr;
public: public:
static const MakeType<IntTag> Type;
IntTag(Buffer *buffer) { IntTag(Buffer *buffer) {
ptr = buffer->get(4); ptr = buffer->get(4);
} }
virtual Type getType() const { virtual const TagType & getType() const {
return Type::Int; return Type;
} }
virtual void print(std::ostream& os, const std::string &) const { virtual void print(std::ostream& os, const std::string &) const {

View file

@ -34,34 +34,29 @@
namespace MinedMap { namespace MinedMap {
namespace NBT { namespace NBT {
class ListTag : public Tag, public std::vector<std::shared_ptr<const Tag>> {
class ListTagBase : public Tag {
public:
virtual Type getType() const {
return Type::List;
}
virtual Type getSubtype() const = 0;
};
template<typename T>
class ListTag : public ListTagBase, public std::vector<std::shared_ptr<const T>> {
private: private:
Type type; const TagType *subtype;
public: public:
ListTag(Type type0, Buffer *buffer) : type(type0) { static const MakeType<ListTag> Type;
ListTag(Buffer *buffer) {
subtype = &getTypeById(buffer->get8());
uint32_t len = buffer->get32(); uint32_t len = buffer->get32();
this->resize(len);
for (uint32_t i = 0; i < len; i++) for (uint32_t i = 0; i < len; i++)
(*this)[i] = std::static_pointer_cast<const T>(Tag::readTag(type, buffer)); push_back(subtype->read(buffer));
} }
virtual Type getSubtype() const { virtual const TagType & getType() const {
return type; return Type;
}
virtual const TagType & getSubtype() const {
return *subtype;
} }
virtual void print(std::ostream& os, const std::string &indent) const { virtual void print(std::ostream& os, const std::string &indent) const {

View file

@ -40,13 +40,16 @@ private:
const uint8_t *ptr; const uint8_t *ptr;
public: public:
static const MakeType<LongArrayTag> Type;
LongArrayTag(Buffer *buffer) { LongArrayTag(Buffer *buffer) {
len = buffer->get32(); len = buffer->get32();
ptr = buffer->get(8*len); ptr = buffer->get(8*len);
} }
virtual Type getType() const { virtual const TagType & getType() const {
return Type::LongArray; return Type;
} }
virtual void print(std::ostream& os, const std::string &indent) const { virtual void print(std::ostream& os, const std::string &indent) const {

View file

@ -37,12 +37,15 @@ private:
const uint8_t *ptr; const uint8_t *ptr;
public: public:
static const MakeType<LongTag> Type;
LongTag(Buffer *buffer) { LongTag(Buffer *buffer) {
ptr = buffer->get(8); ptr = buffer->get(8);
} }
virtual Type getType() const { virtual const TagType & getType() const {
return Type::Long; return Type;
} }
virtual void print(std::ostream& os, const std::string &) const { virtual void print(std::ostream& os, const std::string &) const {

View file

@ -37,12 +37,15 @@ private:
const uint8_t *ptr; const uint8_t *ptr;
public: public:
static const MakeType<ShortTag> Type;
ShortTag(Buffer *buffer) { ShortTag(Buffer *buffer) {
ptr = buffer->get(2); ptr = buffer->get(2);
} }
virtual Type getType() const { virtual const TagType & getType() const {
return Type::Short; return Type;
} }
virtual void print(std::ostream& os, const std::string &) const { virtual void print(std::ostream& os, const std::string &) const {

View file

@ -38,13 +38,16 @@ private:
const uint8_t *ptr; const uint8_t *ptr;
public: public:
static const MakeType<StringTag> Type;
StringTag(Buffer *buffer) { StringTag(Buffer *buffer) {
len = buffer->get16(); len = buffer->get16();
ptr = buffer->get(len); ptr = buffer->get(len);
} }
virtual Type getType() const { virtual const TagType & getType() const {
return Type::String; return Type;
} }
virtual void print(std::ostream& os, const std::string &) const { virtual void print(std::ostream& os, const std::string &) const {

View file

@ -41,176 +41,50 @@
#include "LongArrayTag.hpp" #include "LongArrayTag.hpp"
#include <stdexcept>
namespace MinedMap { namespace MinedMap {
namespace NBT { namespace NBT {
std::shared_ptr<const Tag> Tag::readTag(Type type, Buffer *buffer) { const Tag::MakeType<EndTag> EndTag::Type("End");
switch (type) { const Tag::MakeType<ByteTag> ByteTag::Type("Byte");
case Type::End: const Tag::MakeType<ShortTag> ShortTag::Type("Short");
return std::make_shared<EndTag>(); const Tag::MakeType<IntTag> IntTag::Type("Int");
const Tag::MakeType<LongTag> LongTag::Type("Long");
const Tag::MakeType<FloatTag> FloatTag::Type("Float");
const Tag::MakeType<DoubleTag> DoubleTag::Type("Double");
const Tag::MakeType<ByteArrayTag> ByteArrayTag::Type("ByteArray");
const Tag::MakeType<StringTag> StringTag::Type("String");
const Tag::MakeType<ListTag> ListTag::Type("List");
const Tag::MakeType<CompoundTag> CompoundTag::Type("Compound");
const Tag::MakeType<IntArrayTag> IntArrayTag::Type("IntArray");
const Tag::MakeType<LongArrayTag> LongArrayTag::Type("LongArray");
case Type::Byte:
return std::make_shared<ByteTag>(buffer);
case Type::Short: const std::vector<const TagType *> Tag::types = {
return std::make_shared<ShortTag>(buffer); &EndTag::Type,
&ByteTag::Type,
&ShortTag::Type,
&IntTag::Type,
&LongTag::Type,
&FloatTag::Type,
&DoubleTag::Type,
&ByteArrayTag::Type,
&StringTag::Type,
&ListTag::Type,
&CompoundTag::Type,
&IntArrayTag::Type,
&LongArrayTag::Type,
};
case Type::Int:
return std::make_shared<IntTag>(buffer);
case Type::Long:
return std::make_shared<LongTag>(buffer);
case Type::Float:
return std::make_shared<FloatTag>(buffer);
case Type::Double:
return std::make_shared<DoubleTag>(buffer);
case Type::ByteArray:
return std::make_shared<ByteArrayTag>(buffer);
case Type::String:
return std::make_shared<StringTag>(buffer);
case Type::List:
return readList(buffer);
case Type::Compound:
return std::make_shared<CompoundTag>(buffer);
case Type::IntArray:
return std::make_shared<IntArrayTag>(buffer);
case Type::LongArray:
return std::make_shared<LongArrayTag>(buffer);
default:
throw std::runtime_error("Tag::readTag: unknown tag type");
}
}
std::shared_ptr<const Tag> Tag::readList(Buffer *buffer) {
Type type = static_cast<Type>(buffer->get8());
switch (type) {
case Type::End:
return std::make_shared<ListTag<EndTag>>(type, buffer);
case Type::Byte:
return std::make_shared<ListTag<ByteTag>>(type, buffer);
case Type::Short:
return std::make_shared<ListTag<ShortTag>>(type, buffer);
case Type::Int:
return std::make_shared<ListTag<IntTag>>(type, buffer);
case Type::Long:
return std::make_shared<ListTag<LongTag>>(type, buffer);
case Type::Float:
return std::make_shared<ListTag<FloatTag>>(type, buffer);
case Type::Double:
return std::make_shared<ListTag<DoubleTag>>(type, buffer);
case Type::ByteArray:
return std::make_shared<ListTag<ByteArrayTag>>(type, buffer);
case Type::String:
return std::make_shared<ListTag<StringTag>>(type, buffer);
case Type::List:
return std::make_shared<ListTag<ListTagBase>>(type, buffer);
case Type::Compound:
return std::make_shared<ListTag<CompoundTag>>(type, buffer);
case Type::IntArray:
return std::make_shared<ListTag<IntArrayTag>>(type, buffer);
case Type::LongArray:
return std::make_shared<ListTag<LongArrayTag>>(type, buffer);
default:
throw std::runtime_error("Tag::readList: unknown tag type");
}
}
std::pair<std::string, std::shared_ptr<const Tag>> Tag::readNamedTag(Buffer *buffer) { std::pair<std::string, std::shared_ptr<const Tag>> Tag::readNamedTag(Buffer *buffer) {
Type type = static_cast<Type>(buffer->get8()); const TagType &type = getTypeById(buffer->get8());
if (type == Type::End) if (type == EndTag::Type)
return std::make_pair("", std::shared_ptr<EndTag>(new EndTag())); return std::make_pair("", std::make_shared<EndTag>(buffer));
uint16_t len = buffer->get16(); uint16_t len = buffer->get16();
std::string name(reinterpret_cast<const char*>(buffer->get(len)), len); std::string name(reinterpret_cast<const char*>(buffer->get(len)), len);
return std::make_pair(name, readTag(type, buffer)); return std::make_pair(name, type.read(buffer));
}
std::ostream& operator<<(std::ostream& os, Tag::Type type) {
switch (type) {
case Tag::Type::End:
os << "End";
break;
case Tag::Type::Byte:
os << "Byte";
break;
case Tag::Type::Short:
os << "Short";
break;
case Tag::Type::Int:
os << "Int";
break;
case Tag::Type::Long:
os << "Long";
break;
case Tag::Type::Float:
os << "Float";
break;
case Tag::Type::Double:
os << "Double";
break;
case Tag::Type::ByteArray:
os << "ByteArray";
break;
case Tag::Type::String:
os << "String";
break;
case Tag::Type::List:
os << "List";
break;
case Tag::Type::Compound:
os << "Compound";
break;
case Tag::Type::IntArray:
os << "IntArray";
break;
case Tag::Type::LongArray:
os << "LongArray";
break;
default:
os.setstate(std::ios_base::failbit);
}
return os;
} }
} }

View file

@ -29,6 +29,7 @@
#include <cstdint> #include <cstdint>
#include <memory> #include <memory>
#include <ostream> #include <ostream>
#include <vector>
#include "../Buffer.hpp" #include "../Buffer.hpp"
@ -36,37 +37,61 @@
namespace MinedMap { namespace MinedMap {
namespace NBT { namespace NBT {
class Tag;
class TagType {
public:
TagType() = default;
TagType(const TagType&) = delete;
TagType & operator=(const TagType&) = delete;
virtual const char * getName() const = 0;
virtual std::shared_ptr<const Tag> read(Buffer *buffer) const = 0;
bool operator==(const TagType &type) const {
return this == &type;
}
};
class Tag { class Tag {
private: private:
static std::shared_ptr<const Tag> readList(Buffer *buffer); static const std::vector<const TagType *> types;
public: protected:
enum class Type { template<typename T>
End = 0, class MakeType : public TagType {
Byte = 1, private:
Short = 2, const char *name;
Int = 3,
Long = 4, public:
Float = 5, MakeType(const char *name0) : name(name0) {}
Double = 6,
ByteArray = 7, virtual const char * getName() const {
String = 8, return name;
List = 9, }
Compound = 10,
IntArray = 11, virtual std::shared_ptr<const Tag> read(Buffer *buffer) const {
LongArray = 12, return std::make_shared<T>(buffer);
}
}; };
static std::shared_ptr<const Tag> readTag(Type type, Buffer *buffer);
static const TagType & getTypeById(uint8_t id) {
return *types.at(id);
}
public:
static std::pair<std::string, std::shared_ptr<const Tag>> readNamedTag(Buffer *buffer); static std::pair<std::string, std::shared_ptr<const Tag>> readNamedTag(Buffer *buffer);
virtual Type getType() const = 0; virtual const TagType & getType() const = 0;
virtual void print(std::ostream& os, const std::string &indent) const = 0; virtual void print(std::ostream& os, const std::string &indent) const = 0;
virtual ~Tag() {} virtual ~Tag() {}
}; };
std::ostream& operator<<(std::ostream& os, Tag::Type type); static inline std::ostream& operator<<(std::ostream& os, const TagType &type) {
return os << type.getName();
}
static inline std::ostream& operator<<(std::ostream& os, const Tag &tag) { static inline std::ostream& operator<<(std::ostream& os, const Tag &tag) {
os << tag.getType() << " "; os << tag.getType() << " ";

View file

@ -44,8 +44,9 @@ Chunk::Chunk(const ChunkData *data) {
std::shared_ptr<const NBT::ByteTag> lightPopulatedTag = level->get<NBT::ByteTag>("LightPopulated"); std::shared_ptr<const NBT::ByteTag> lightPopulatedTag = level->get<NBT::ByteTag>("LightPopulated");
bool lightPopulated = lightPopulatedTag && lightPopulatedTag->getValue(); bool lightPopulated = lightPopulatedTag && lightPopulatedTag->getValue();
sections = assertValue(level->get<NBT::ListTag<NBT::CompoundTag>>("Sections")); sections = assertValue(level->get<NBT::ListTag>("Sections"));
maxY = (assertValue(sections->back()->get<NBT::ByteTag>("Y"))->getValue() + 1) * SIZE; const NBT::CompoundTag *lastSection = assertValue(dynamic_cast<const NBT::CompoundTag *>(sections->back().get()));
maxY = (assertValue(lastSection->get<NBT::ByteTag>("Y"))->getValue() + 1) * SIZE;
std::shared_ptr<const NBT::ByteArrayTag> biomeTag = assertValue(level->get<NBT::ByteArrayTag>("Biomes")); std::shared_ptr<const NBT::ByteArrayTag> biomeTag = assertValue(level->get<NBT::ByteArrayTag>("Biomes"));
@ -65,7 +66,8 @@ Chunk::Chunk(const ChunkData *data) {
std::memset(blockBlockLight.get(), 0, maxY * SIZE * SIZE / 2); std::memset(blockBlockLight.get(), 0, maxY * SIZE * SIZE / 2);
for (auto &section : *sections) { for (auto &sectionTag : *sections) {
const NBT::CompoundTag *section = assertValue(dynamic_cast<const NBT::CompoundTag *>(sectionTag.get()));
std::shared_ptr<const NBT::ByteArrayTag> blocks = assertValue(section->get<NBT::ByteArrayTag>("Blocks")); std::shared_ptr<const NBT::ByteArrayTag> blocks = assertValue(section->get<NBT::ByteArrayTag>("Blocks"));
std::shared_ptr<const NBT::ByteArrayTag> data = assertValue(section->get<NBT::ByteArrayTag>("Data")); std::shared_ptr<const NBT::ByteArrayTag> data = assertValue(section->get<NBT::ByteArrayTag>("Data"));
size_t Y = assertValue(section->get<NBT::ByteTag>("Y"))->getValue(); size_t Y = assertValue(section->get<NBT::ByteTag>("Y"))->getValue();

View file

@ -52,7 +52,7 @@ public:
private: private:
std::shared_ptr<const NBT::CompoundTag> level; std::shared_ptr<const NBT::CompoundTag> level;
std::shared_ptr<const NBT::ListTag<NBT::CompoundTag>> sections; std::shared_ptr<const NBT::ListTag> sections;
unsigned maxY; unsigned maxY;
@ -107,7 +107,7 @@ public:
return *level; return *level;
} }
const NBT::ListTag<NBT::CompoundTag> & getSections() const { const NBT::ListTag & getSections() const {
return *sections; return *sections;
} }