From 8f75d58deda62215f80c7665035ee6dff1a14e8d Mon Sep 17 00:00:00 2001 From: DualFroz Date: Tue, 25 Aug 2026 17:31:54 +0200 Subject: [PATCH] feat(core): declare NBT tag tree read and write interface --- core/include/vf/Nbt.h | 103 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) create mode 100644 core/include/vf/Nbt.h diff --git a/core/include/vf/Nbt.h b/core/include/vf/Nbt.h new file mode 100644 index 0000000..8f4b9e4 --- /dev/null +++ b/core/include/vf/Nbt.h @@ -0,0 +1,103 @@ +#pragma once + +#include +#include +#include +#include +#include +#include "Chunk.h" + +namespace vf { + +enum class NbtType : uint8_t { + End = 0, + Byte = 1, + Short = 2, + Int = 3, + Long = 4, + Float = 5, + Double = 6, + String = 7, + ByteArray = 8, + IntArray = 9, + List = 10, + Compound = 11, +}; + +class NbtTag; + +using NbtList = std::vector; +using NbtCompound = std::vector>; + +// A single NBT-like tag: one of the scalar/array/container payloads above. +// Containers (List, Compound) hold NbtTag by value, so a whole tree round +// trips as one self-contained object. +class NbtTag { +public: + NbtTag() = default; + + static NbtTag makeByte(int8_t v); + static NbtTag makeShort(int16_t v); + static NbtTag makeInt(int32_t v); + static NbtTag makeLong(int64_t v); + static NbtTag makeFloat(float v); + static NbtTag makeDouble(double v); + static NbtTag makeString(std::string v); + static NbtTag makeByteArray(std::vector v); + static NbtTag makeIntArray(std::vector v); + static NbtTag makeList(NbtType elementType); + static NbtTag makeCompound(); + + NbtType kind() const { return type; } + NbtType listElementType() const { return elementType; } + + int8_t asByte() const { return static_cast(integerValue); } + int16_t asShort() const { return static_cast(integerValue); } + int32_t asInt() const { return static_cast(integerValue); } + int64_t asLong() const { return integerValue; } + float asFloat() const { return static_cast(floatingValue); } + double asDouble() const { return floatingValue; } + const std::string& asString() const { return stringValue; } + const std::vector& asByteArray() const { return byteArrayValue; } + const std::vector& asIntArray() const { return intArrayValue; } + + NbtList& asList() { return listValue; } + const NbtList& asList() const { return listValue; } + void listAppend(NbtTag tag) { listValue.push_back(std::move(tag)); } + + NbtCompound& asCompound() { return compoundValue; } + const NbtCompound& asCompound() const { return compoundValue; } + void put(const std::string& key, NbtTag tag); + const NbtTag* find(const std::string& key) const; + +private: + NbtType type = NbtType::End; + NbtType elementType = NbtType::End; + int64_t integerValue = 0; + double floatingValue = 0.0; + std::string stringValue; + std::vector byteArrayValue; + std::vector intArrayValue; + NbtList listValue; + NbtCompound compoundValue; +}; + +// Big-endian binary encoding: tagType(1) + nameLen(u16) + name + payload, +// recursively. Compounds are terminated by an End tag; lists carry a shared +// element type and a u32 count instead of per-element headers. +std::vector serializeNbt(const std::string& rootName, const NbtTag& root); +bool deserializeNbt(const uint8_t* data, std::size_t size, std::string& rootName, NbtTag& root); + +// Palette-based section encoding (format v4): the distinct blocks used by a +// chunkVolume-sized section, followed by fixed-width bit-packed indices into +// that palette (LSB-first within each byte). +struct NbtPaletteSection { + std::vector palette; + std::vector packedIndices; + int bitsPerIndex = 0; +}; + +NbtPaletteSection encodeSectionPalette(const std::array& blocks); +void decodeSectionPalette(const NbtPaletteSection& section, std::array& outBlocks); + +} // namespace vf