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;
public:
static const MakeType<ByteArrayTag> Type;
ByteArrayTag(Buffer *buffer) {
len = buffer->get32();
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 {

View file

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

View file

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

View file

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

View file

@ -34,10 +34,13 @@ namespace NBT {
class EndTag : public Tag {
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 {

View file

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

View file

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

View file

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

View file

@ -34,34 +34,29 @@
namespace MinedMap {
namespace NBT {
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>> {
class ListTag : public Tag, public std::vector<std::shared_ptr<const Tag>> {
private:
Type type;
const TagType *subtype;
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();
this->resize(len);
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 {
return type;
virtual const TagType & getType() const {
return Type;
}
virtual const TagType & getSubtype() const {
return *subtype;
}
virtual void print(std::ostream& os, const std::string &indent) const {

View file

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

View file

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

View file

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

View file

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

View file

@ -41,176 +41,50 @@
#include "LongArrayTag.hpp"
#include <stdexcept>
namespace MinedMap {
namespace NBT {
std::shared_ptr<const Tag> Tag::readTag(Type type, Buffer *buffer) {
switch (type) {
case Type::End:
return std::make_shared<EndTag>();
const Tag::MakeType<EndTag> EndTag::Type("End");
const Tag::MakeType<ByteTag> ByteTag::Type("Byte");
const Tag::MakeType<ShortTag> ShortTag::Type("Short");
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:
return std::make_shared<ShortTag>(buffer);
const std::vector<const TagType *> Tag::types = {
&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) {
Type type = static_cast<Type>(buffer->get8());
if (type == Type::End)
return std::make_pair("", std::shared_ptr<EndTag>(new EndTag()));
const TagType &type = getTypeById(buffer->get8());
if (type == EndTag::Type)
return std::make_pair("", std::make_shared<EndTag>(buffer));
uint16_t len = buffer->get16();
std::string name(reinterpret_cast<const char*>(buffer->get(len)), len);
return std::make_pair(name, readTag(type, 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;
return std::make_pair(name, type.read(buffer));
}
}

View file

@ -29,6 +29,7 @@
#include <cstdint>
#include <memory>
#include <ostream>
#include <vector>
#include "../Buffer.hpp"
@ -36,37 +37,61 @@
namespace MinedMap {
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 {
private:
static std::shared_ptr<const Tag> readList(Buffer *buffer);
static const std::vector<const TagType *> types;
public:
enum class Type {
End = 0,
Byte = 1,
Short = 2,
Int = 3,
Long = 4,
Float = 5,
Double = 6,
ByteArray = 7,
String = 8,
List = 9,
Compound = 10,
IntArray = 11,
LongArray = 12,
protected:
template<typename T>
class MakeType : public TagType {
private:
const char *name;
public:
MakeType(const char *name0) : name(name0) {}
virtual const char * getName() const {
return name;
}
virtual std::shared_ptr<const Tag> read(Buffer *buffer) const {
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);
virtual Type getType() const = 0;
virtual const TagType & getType() const = 0;
virtual void print(std::ostream& os, const std::string &indent) const = 0;
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) {
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");
bool lightPopulated = lightPopulatedTag && lightPopulatedTag->getValue();
sections = assertValue(level->get<NBT::ListTag<NBT::CompoundTag>>("Sections"));
maxY = (assertValue(sections->back()->get<NBT::ByteTag>("Y"))->getValue() + 1) * SIZE;
sections = assertValue(level->get<NBT::ListTag>("Sections"));
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"));
@ -65,7 +66,8 @@ Chunk::Chunk(const ChunkData *data) {
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> data = assertValue(section->get<NBT::ByteArrayTag>("Data"));
size_t Y = assertValue(section->get<NBT::ByteTag>("Y"))->getValue();

View file

@ -52,7 +52,7 @@ public:
private:
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;
@ -107,7 +107,7 @@ public:
return *level;
}
const NBT::ListTag<NBT::CompoundTag> & getSections() const {
const NBT::ListTag & getSections() const {
return *sections;
}