#include "vf/Nbt.h" #include namespace vf { NbtTag NbtTag::makeByte(int8_t v) { NbtTag tag; tag.type = NbtType::Byte; tag.integerValue = v; return tag; } NbtTag NbtTag::makeShort(int16_t v) { NbtTag tag; tag.type = NbtType::Short; tag.integerValue = v; return tag; } NbtTag NbtTag::makeInt(int32_t v) { NbtTag tag; tag.type = NbtType::Int; tag.integerValue = v; return tag; } NbtTag NbtTag::makeLong(int64_t v) { NbtTag tag; tag.type = NbtType::Long; tag.integerValue = v; return tag; } NbtTag NbtTag::makeFloat(float v) { NbtTag tag; tag.type = NbtType::Float; tag.floatingValue = v; return tag; } NbtTag NbtTag::makeDouble(double v) { NbtTag tag; tag.type = NbtType::Double; tag.floatingValue = v; return tag; } NbtTag NbtTag::makeString(std::string v) { NbtTag tag; tag.type = NbtType::String; tag.stringValue = std::move(v); return tag; } NbtTag NbtTag::makeByteArray(std::vector v) { NbtTag tag; tag.type = NbtType::ByteArray; tag.byteArrayValue = std::move(v); return tag; } NbtTag NbtTag::makeIntArray(std::vector v) { NbtTag tag; tag.type = NbtType::IntArray; tag.intArrayValue = std::move(v); return tag; } NbtTag NbtTag::makeList(NbtType elementType) { NbtTag tag; tag.type = NbtType::List; tag.elementType = elementType; return tag; } NbtTag NbtTag::makeCompound() { NbtTag tag; tag.type = NbtType::Compound; return tag; } void NbtTag::put(const std::string& key, NbtTag tag) { for (auto& entry : compoundValue) { if (entry.first == key) { entry.second = std::move(tag); return; } } compoundValue.emplace_back(key, std::move(tag)); } const NbtTag* NbtTag::find(const std::string& key) const { for (const auto& entry : compoundValue) { if (entry.first == key) return &entry.second; } return nullptr; } namespace { void writeU8(std::vector& out, uint8_t v) { out.push_back(v); } void writeU16(std::vector& out, uint16_t v) { out.push_back(static_cast((v >> 8) & 0xFFu)); out.push_back(static_cast(v & 0xFFu)); } void writeU32(std::vector& out, uint32_t v) { for (int shift = 24; shift >= 0; shift -= 8) out.push_back(static_cast((v >> shift) & 0xFFu)); } void writeU64(std::vector& out, uint64_t v) { for (int shift = 56; shift >= 0; shift -= 8) out.push_back(static_cast((v >> shift) & 0xFFu)); } void writeString(std::vector& out, const std::string& s) { writeU16(out, static_cast(s.size())); out.insert(out.end(), s.begin(), s.end()); } void writePayload(std::vector& out, const NbtTag& tag); void writeNamedTag(std::vector& out, const std::string& name, const NbtTag& tag) { out.push_back(static_cast(tag.kind())); writeString(out, name); writePayload(out, tag); } void writePayload(std::vector& out, const NbtTag& tag) { switch (tag.kind()) { case NbtType::Byte: writeU8(out, static_cast(tag.asByte())); break; case NbtType::Short: writeU16(out, static_cast(tag.asShort())); break; case NbtType::Int: writeU32(out, static_cast(tag.asInt())); break; case NbtType::Long: writeU64(out, static_cast(tag.asLong())); break; case NbtType::Float: { float f = tag.asFloat(); uint32_t bits; std::memcpy(&bits, &f, sizeof(bits)); writeU32(out, bits); break; } case NbtType::Double: { double d = tag.asDouble(); uint64_t bits; std::memcpy(&bits, &d, sizeof(bits)); writeU64(out, bits); break; } case NbtType::String: writeString(out, tag.asString()); break; case NbtType::ByteArray: { const std::vector& arr = tag.asByteArray(); writeU32(out, static_cast(arr.size())); for (int8_t v : arr) writeU8(out, static_cast(v)); break; } case NbtType::IntArray: { const std::vector& arr = tag.asIntArray(); writeU32(out, static_cast(arr.size())); for (int32_t v : arr) writeU32(out, static_cast(v)); break; } case NbtType::List: { out.push_back(static_cast(tag.listElementType())); const NbtList& list = tag.asList(); writeU32(out, static_cast(list.size())); for (const NbtTag& element : list) writePayload(out, element); break; } case NbtType::Compound: { for (const auto& entry : tag.asCompound()) writeNamedTag(out, entry.first, entry.second); out.push_back(static_cast(NbtType::End)); break; } case NbtType::End: break; } } struct NbtReader { const uint8_t* data; std::size_t size; std::size_t cursor = 0; bool remaining(std::size_t n) const { return cursor + n <= size; } bool readU8(uint8_t& out) { if (!remaining(1)) return false; out = data[cursor++]; return true; } bool readU16(uint16_t& out) { if (!remaining(2)) return false; out = static_cast((data[cursor] << 8) | data[cursor + 1]); cursor += 2; return true; } bool readU32(uint32_t& out) { if (!remaining(4)) return false; out = 0; for (int i = 0; i < 4; ++i) out = (out << 8) | data[cursor + static_cast(i)]; cursor += 4; return true; } bool readU64(uint64_t& out) { if (!remaining(8)) return false; out = 0; for (int i = 0; i < 8; ++i) out = (out << 8) | data[cursor + static_cast(i)]; cursor += 8; return true; } bool readString(std::string& out) { uint16_t len = 0; if (!readU16(len)) return false; if (!remaining(len)) return false; out.assign(reinterpret_cast(data + cursor), len); cursor += len; return true; } }; bool readPayload(NbtReader& reader, NbtType type, NbtTag& out); bool readNamedTag(NbtReader& reader, std::string& name, NbtTag& out) { uint8_t rawType = 0; if (!reader.readU8(rawType)) return false; if (!reader.readString(name)) return false; return readPayload(reader, static_cast(rawType), out); } bool readPayload(NbtReader& reader, NbtType type, NbtTag& out) { switch (type) { case NbtType::Byte: { uint8_t v; if (!reader.readU8(v)) return false; out = NbtTag::makeByte(static_cast(v)); return true; } case NbtType::Short: { uint16_t v; if (!reader.readU16(v)) return false; out = NbtTag::makeShort(static_cast(v)); return true; } case NbtType::Int: { uint32_t v; if (!reader.readU32(v)) return false; out = NbtTag::makeInt(static_cast(v)); return true; } case NbtType::Long: { uint64_t v; if (!reader.readU64(v)) return false; out = NbtTag::makeLong(static_cast(v)); return true; } case NbtType::Float: { uint32_t bits; if (!reader.readU32(bits)) return false; float f; std::memcpy(&f, &bits, sizeof(f)); out = NbtTag::makeFloat(f); return true; } case NbtType::Double: { uint64_t bits; if (!reader.readU64(bits)) return false; double d; std::memcpy(&d, &bits, sizeof(d)); out = NbtTag::makeDouble(d); return true; } case NbtType::String: { std::string s; if (!reader.readString(s)) return false; out = NbtTag::makeString(std::move(s)); return true; } case NbtType::ByteArray: { uint32_t count; if (!reader.readU32(count)) return false; std::vector arr; arr.reserve(count); for (uint32_t i = 0; i < count; ++i) { uint8_t v; if (!reader.readU8(v)) return false; arr.push_back(static_cast(v)); } out = NbtTag::makeByteArray(std::move(arr)); return true; } case NbtType::IntArray: { uint32_t count; if (!reader.readU32(count)) return false; std::vector arr; arr.reserve(count); for (uint32_t i = 0; i < count; ++i) { uint32_t v; if (!reader.readU32(v)) return false; arr.push_back(static_cast(v)); } out = NbtTag::makeIntArray(std::move(arr)); return true; } case NbtType::List: { uint8_t rawElementType; if (!reader.readU8(rawElementType)) return false; NbtType elementType = static_cast(rawElementType); uint32_t count; if (!reader.readU32(count)) return false; out = NbtTag::makeList(elementType); for (uint32_t i = 0; i < count; ++i) { NbtTag element; if (!readPayload(reader, elementType, element)) return false; out.listAppend(std::move(element)); } return true; } case NbtType::Compound: { out = NbtTag::makeCompound(); for (;;) { uint8_t rawChildType; if (!reader.readU8(rawChildType)) return false; NbtType childType = static_cast(rawChildType); if (childType == NbtType::End) return true; std::string childName; if (!reader.readString(childName)) return false; NbtTag child; if (!readPayload(reader, childType, child)) return false; out.put(childName, std::move(child)); } } case NbtType::End: return true; } return false; } } // namespace std::vector serializeNbt(const std::string& rootName, const NbtTag& root) { std::vector out; writeNamedTag(out, rootName, root); return out; } bool deserializeNbt(const uint8_t* data, std::size_t size, std::string& rootName, NbtTag& root) { NbtReader reader{data, size}; return readNamedTag(reader, rootName, root); } NbtPaletteSection encodeSectionPalette(const std::array& blocks) { NbtPaletteSection section; std::array(Block::Count)> indexOf; indexOf.fill(-1); std::vector indices; indices.reserve(blocks.size()); for (Block block : blocks) { int& slot = indexOf[static_cast(block)]; if (slot < 0) { slot = static_cast(section.palette.size()); section.palette.push_back(block); } indices.push_back(slot); } int bits = 1; while ((1 << bits) < static_cast(section.palette.size())) ++bits; section.bitsPerIndex = bits; std::size_t totalBits = static_cast(bits) * indices.size(); section.packedIndices.assign((totalBits + 7) / 8, 0); std::size_t bitCursor = 0; for (int index : indices) { for (int b = 0; b < bits; ++b) { if ((index >> b) & 1) { std::size_t byteIndex = bitCursor / 8; int bitOffset = static_cast(bitCursor % 8); section.packedIndices[byteIndex] |= static_cast(1u << bitOffset); } ++bitCursor; } } return section; } void decodeSectionPalette(const NbtPaletteSection& section, std::array& outBlocks) { std::size_t bitCursor = 0; for (std::size_t i = 0; i < outBlocks.size(); ++i) { int index = 0; for (int b = 0; b < section.bitsPerIndex; ++b) { std::size_t byteIndex = bitCursor / 8; int bitOffset = static_cast(bitCursor % 8); if (section.packedIndices[byteIndex] & static_cast(1u << bitOffset)) index |= (1 << b); ++bitCursor; } outBlocks[i] = section.palette[static_cast(index)]; } } } // namespace vf