Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support unnamed bnbt tags #89

Merged
merged 11 commits into from
Oct 14, 2024
16 changes: 12 additions & 4 deletions src/amulet_nbt/__init__.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,15 @@ class AbstractBaseTag:
compressed: bool = True,
little_endian: bool = False,
string_encoding: StringEncoding = mutf8_encoding,
name: str | bytes = b"",
name: str | bytes | None = b"",
) -> bytes:
"""Get the data in binary NBT format.

:param preset: A class containing endianness and encoding presets.
:param compressed: Should the bytes be compressed with gzip.
:param little_endian: Should the bytes be saved in little endian format. Ignored if preset is defined.
:param string_encoding: The StringEncoding to use. Ignored if preset is defined.
:param name: The root tag name.
:param name: The root tag name, or `None` for unnamed tag.
:return: The binary NBT representation of the class.
"""

Expand All @@ -142,7 +142,7 @@ class AbstractBaseTag:
compressed: bool = True,
little_endian: bool = False,
string_encoding: StringEncoding = mutf8_encoding,
name: str | bytes = b"",
name: str | bytes | None = b"",
) -> bytes:
"""Convert the data to the binary NBT format. Optionally write to a file.

Expand All @@ -155,7 +155,7 @@ class AbstractBaseTag:
:param compressed: Should the bytes be compressed with gzip.
:param little_endian: Should the bytes be saved in little endian format. Ignored if preset is defined.
:param string_encoding: The StringEncoding to use. Ignored if preset is defined.
:param name: The root tag name.
:param name: The root tag name, or `None` for unnamed tag.
shBLOCK marked this conversation as resolved.
Show resolved Hide resolved
:return: The binary NBT representation of the class.
"""

Expand Down Expand Up @@ -1592,12 +1592,14 @@ def read_nbt(
filepath_or_buffer: str | bytes | memoryview | _Readable | None,
*,
preset: EncodingPreset | None = None,
named: bool = True,
read_offset: ReadOffset | None = None,
) -> NamedTag:
"""Load one binary NBT object.

:param filepath_or_buffer: A string path to a file on disk, a bytes or memory view object containing the binary NBT or a file-like object to read the binary data from.
:param preset: The encoding preset. If this is defined little_endian and string_encoding have no effect.
:param named: If the tag to read is named, if not, return NamedTag with empty name.
:param read_offset: Optional ReadOffset object to get read end offset.
:raises: IndexError if the data is not long enough.
"""
Expand All @@ -1609,6 +1611,7 @@ def read_nbt(
compressed: bool = True,
little_endian: bool = False,
string_encoding: StringEncoding = mutf8_encoding,
named: bool = True,
read_offset: ReadOffset | None = None,
) -> NamedTag:
"""Load one binary NBT object.
Expand All @@ -1617,6 +1620,7 @@ def read_nbt(
:param compressed: Is the binary data gzip compressed.
:param little_endian: Are the numerical values stored as little endian. True for Bedrock, False for Java.
:param string_encoding: The bytes decoder function to parse strings. mutf8_encoding for Java, utf8_escape_encoding for Bedrock.
:param named: If the tag to read is named, if not, return NamedTag with empty name.
:param read_offset: Optional ReadOffset object to get read end offset.
:raises: IndexError if the data is not long enough.
"""
Expand All @@ -1627,13 +1631,15 @@ def read_nbt_array(
*,
count: int = 1,
preset: EncodingPreset | None = None,
named: bool = True,
read_offset: ReadOffset | None = None,
) -> list[NamedTag]:
"""Load an array of binary NBT objects from a contiguous buffer.

:param filepath_or_buffer: A string path to a file on disk, a bytes or memory view object containing the binary NBT or a file-like object to read the binary data from.
:param count: The number of binary NBT objects to read. Use -1 to exhaust the buffer.
:param preset: The encoding preset. If this is defined little_endian and string_encoding have no effect.
:param named: If the tags to read are named, if not, return NamedTags with empty name.
:param read_offset: Optional ReadOffset object to get read end offset.
:raises: IndexError if the data is not long enough.
"""
Expand All @@ -1646,6 +1652,7 @@ def read_nbt_array(
compressed: bool = True,
little_endian: bool = False,
string_encoding: StringEncoding = mutf8_encoding,
named: bool = True,
read_offset: ReadOffset | None = None,
) -> list[NamedTag]:
"""Load an array of binary NBT objects from a contiguous buffer.
Expand All @@ -1655,6 +1662,7 @@ def read_nbt_array(
:param compressed: Is the binary data gzip compressed. This only supports the whole buffer compressed as one.
:param little_endian: Are the numerical values stored as little endian. True for Bedrock, False for Java.
:param string_encoding: The bytes decoder function to parse strings. mutf8.decode_modified_utf8 for Java, amulet_nbt.utf8_escape_decoder for Bedrock.
:param named: If the tags to read are named, if not, return NamedTags with empty name.
:param read_offset: Optional ReadOffset object to get read end offset.
:raises: IndexError if the data is not long enough.
"""
Expand Down
31 changes: 18 additions & 13 deletions src/amulet_nbt/cpp/nbt_encoding/binary/read_binary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,41 +163,46 @@ inline AmuletNBT::TagNode read_node(AmuletNBT::BinaryReader& reader, std::uint8_


namespace AmuletNBT {
AmuletNBT::NamedTag read_nbt(AmuletNBT::BinaryReader& reader){
AmuletNBT::NamedTag read_nbt(AmuletNBT::BinaryReader& reader, bool named){
std::uint8_t tag_id = reader.readNumeric<std::uint8_t>();
std::string name = read_string_tag(reader);
std::string name = named ? read_string_tag(reader) : "";
AmuletNBT::TagNode node = read_node(reader, tag_id);
return AmuletNBT::NamedTag(name, node);
}

// Read one named tag from the string at position offset.
AmuletNBT::NamedTag read_nbt(const std::string& raw, std::endian endianness, AmuletNBT::StringDecode string_decode, size_t& offset){
// Read one (un)named tag from the string at position offset.
AmuletNBT::NamedTag read_nbt(const std::string& raw, std::endian endianness, AmuletNBT::StringDecode string_decode, bool named, size_t& offset){
AmuletNBT::BinaryReader reader(raw, offset, endianness, string_decode);
return read_nbt(reader);
return read_nbt(reader, named);
}

// Read one (un)named tag from the string.
AmuletNBT::NamedTag read_nbt(const std::string& raw, std::endian endianness, AmuletNBT::StringDecode string_decode, bool named){
size_t offset = 0;
return read_nbt(raw, endianness, string_decode, named, offset);
}

// Read one named tag from the string.
AmuletNBT::NamedTag read_nbt(const std::string& raw, std::endian endianness, AmuletNBT::StringDecode string_decode){
size_t offset = 0;
return read_nbt(raw, endianness, string_decode, offset);
return read_nbt(raw, endianness, string_decode, true);
gentlegiantJGC marked this conversation as resolved.
Show resolved Hide resolved
}

// Read count named tags from the string at position offset.
std::vector<AmuletNBT::NamedTag> read_nbt_array(const std::string& raw, std::endian endianness, AmuletNBT::StringDecode string_decode, size_t& offset, size_t count){
// Read count (un)named tags from the string at position offset.
std::vector<AmuletNBT::NamedTag> read_nbt_array(const std::string& raw, std::endian endianness, AmuletNBT::StringDecode string_decode, bool named, size_t& offset, size_t count){
AmuletNBT::BinaryReader reader(raw, offset, endianness, string_decode);
std::vector<AmuletNBT::NamedTag> out;
for (size_t i = 0; i < count; i++){
out.push_back(read_nbt(reader));
out.push_back(read_nbt(reader, named));
}
return out;
}

// Read all named tags from the string at position offset.
std::vector<AmuletNBT::NamedTag> read_nbt_array(const std::string& raw, std::endian endianness, AmuletNBT::StringDecode string_decode, size_t& offset){
// Read all (un)named tags from the string at position offset.
std::vector<AmuletNBT::NamedTag> read_nbt_array(const std::string& raw, std::endian endianness, AmuletNBT::StringDecode string_decode, bool named, size_t& offset){
AmuletNBT::BinaryReader reader(raw, offset, endianness, string_decode);
std::vector<AmuletNBT::NamedTag> out;
while (reader.has_more_data()){
out.push_back(read_nbt(reader));
out.push_back(read_nbt(reader, named));
}
return out;
}
Expand Down
59 changes: 30 additions & 29 deletions src/amulet_nbt/cpp/nbt_encoding/binary/write_binary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
#include <cstdint>
#include <memory>
#include <vector>
#include <optional>

#include <amulet_nbt/common.hpp>
#include <amulet_nbt/tag/int.hpp>
Expand Down Expand Up @@ -167,9 +168,9 @@ template <
bool
> = true
>
inline void write_name_and_tag(AmuletNBT::BinaryWriter& writer, const std::string& name, const T& tag){
inline void write_name_and_tag(AmuletNBT::BinaryWriter& writer, const std::optional<std::string>& name, const T& tag){
writer.writeNumeric<std::uint8_t>(AmuletNBT::tag_id_v<T>);
write_string(writer, name);
if (name) write_string(writer, *name);
write_payload(writer, tag);
}

Expand All @@ -184,7 +185,7 @@ template <
bool
> = true
>
inline void write_name_and_tag(AmuletNBT::BinaryWriter & writer, const std::string & name, const T tag) {
inline void write_name_and_tag(AmuletNBT::BinaryWriter & writer, const std::optional<std::string>& name, const T tag) {
write_name_and_tag<typename T::element_type>(writer, name, *tag);
}

Expand All @@ -193,7 +194,7 @@ template <
typename T,
std::enable_if_t<std::is_same_v<T, AmuletNBT::TagNode>, bool> = true
>
inline void write_name_and_tag(AmuletNBT::BinaryWriter& writer, const std::string& name, const AmuletNBT::TagNode& node){
inline void write_name_and_tag(AmuletNBT::BinaryWriter& writer, const std::optional<std::string>& name, const AmuletNBT::TagNode& node){
std::visit([&writer, &name](auto&& tag) {
using tagT = std::decay_t<decltype(tag)>;
write_name_and_tag<tagT>(writer, name, tag);
Expand All @@ -211,47 +212,47 @@ inline void write_payload<AmuletNBT::CompoundTag>(AmuletNBT::BinaryWriter& write


template <typename T>
inline std::string _write_nbt(const std::string& name, const T& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
inline std::string _write_nbt(const std::optional<std::string>& name, const T& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
AmuletNBT::BinaryWriter writer(endianness, string_encode);
write_name_and_tag<T>(writer, name, tag);
return writer.getBuffer();
}

namespace AmuletNBT {
void write_nbt(BinaryWriter& writer, const std::string& name, const ByteTag& tag) {
void write_nbt(BinaryWriter& writer, const std::optional<std::string>& name, const ByteTag& tag) {
write_name_and_tag<ByteTag>(writer, name, tag);
}
void write_nbt(BinaryWriter& writer, const std::string& name, const ShortTag& tag) {
void write_nbt(BinaryWriter& writer, const std::optional<std::string>& name, const ShortTag& tag) {
write_name_and_tag<ShortTag>(writer, name, tag);
}
void write_nbt(BinaryWriter& writer, const std::string& name, const IntTag& tag) {
void write_nbt(BinaryWriter& writer, const std::optional<std::string>& name, const IntTag& tag) {
write_name_and_tag<IntTag>(writer, name, tag);
}
void write_nbt(BinaryWriter& writer, const std::string& name, const LongTag& tag) {
void write_nbt(BinaryWriter& writer, const std::optional<std::string>& name, const LongTag& tag) {
write_name_and_tag<LongTag>(writer, name, tag);
}
void write_nbt(BinaryWriter& writer, const std::string& name, const FloatTag& tag) {
void write_nbt(BinaryWriter& writer, const std::optional<std::string>& name, const FloatTag& tag) {
write_name_and_tag<FloatTag>(writer, name, tag);
}
void write_nbt(BinaryWriter& writer, const std::string& name, const DoubleTag& tag) {
void write_nbt(BinaryWriter& writer, const std::optional<std::string>& name, const DoubleTag& tag) {
write_name_and_tag<DoubleTag>(writer, name, tag);
}
void write_nbt(BinaryWriter& writer, const std::string& name, const ByteArrayTag& tag) {
void write_nbt(BinaryWriter& writer, const std::optional<std::string>& name, const ByteArrayTag& tag) {
write_name_and_tag<ByteArrayTag>(writer, name, tag);
}
void write_nbt(BinaryWriter& writer, const std::string& name, const StringTag& tag) {
void write_nbt(BinaryWriter& writer, const std::optional<std::string>& name, const StringTag& tag) {
write_name_and_tag<StringTag>(writer, name, tag);
}
void write_nbt(BinaryWriter& writer, const std::string& name, const ListTag& tag) {
void write_nbt(BinaryWriter& writer, const std::optional<std::string>& name, const ListTag& tag) {
write_name_and_tag<ListTag>(writer, name, tag);
}
void write_nbt(BinaryWriter& writer, const std::string& name, const CompoundTag& tag) {
void write_nbt(BinaryWriter& writer, const std::optional<std::string>& name, const CompoundTag& tag) {
write_name_and_tag<CompoundTag>(writer, name, tag);
}
void write_nbt(BinaryWriter& writer, const std::string& name, const IntArrayTag& tag) {
void write_nbt(BinaryWriter& writer, const std::optional<std::string>& name, const IntArrayTag& tag) {
write_name_and_tag<IntArrayTag>(writer, name, tag);
}
void write_nbt(BinaryWriter& writer, const std::string& name, const LongArrayTag& tag) {
void write_nbt(BinaryWriter& writer, const std::optional<std::string>& name, const LongArrayTag& tag) {
write_name_and_tag<LongArrayTag>(writer, name, tag);
}
void write_nbt(BinaryWriter& writer, const std::string& name, const TagNode& tag) {
Expand All @@ -261,40 +262,40 @@ namespace AmuletNBT {
write_name_and_tag<TagNode>(writer, tag.name, tag.tag_node);
}

std::string write_nbt(const std::string& name, const AmuletNBT::ByteTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
std::string write_nbt(const std::optional<std::string>& name, const AmuletNBT::ByteTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
return _write_nbt(name, tag, endianness, string_encode);
};
std::string write_nbt(const std::string& name, const AmuletNBT::ShortTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
std::string write_nbt(const std::optional<std::string>& name, const AmuletNBT::ShortTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
return _write_nbt(name, tag, endianness, string_encode);
};
std::string write_nbt(const std::string& name, const AmuletNBT::IntTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
std::string write_nbt(const std::optional<std::string>& name, const AmuletNBT::IntTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
return _write_nbt(name, tag, endianness, string_encode);
};
std::string write_nbt(const std::string& name, const AmuletNBT::LongTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
std::string write_nbt(const std::optional<std::string>& name, const AmuletNBT::LongTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
return _write_nbt(name, tag, endianness, string_encode);
};
std::string write_nbt(const std::string& name, const AmuletNBT::FloatTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
std::string write_nbt(const std::optional<std::string>& name, const AmuletNBT::FloatTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
return _write_nbt(name, tag, endianness, string_encode);
};
std::string write_nbt(const std::string& name, const AmuletNBT::DoubleTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
std::string write_nbt(const std::optional<std::string>& name, const AmuletNBT::DoubleTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
return _write_nbt(name, tag, endianness, string_encode);
};
std::string write_nbt(const std::string& name, const AmuletNBT::ByteArrayTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
std::string write_nbt(const std::optional<std::string>& name, const AmuletNBT::ByteArrayTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
return _write_nbt(name, tag, endianness, string_encode);
};
std::string write_nbt(const std::string& name, const AmuletNBT::StringTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
std::string write_nbt(const std::optional<std::string>& name, const AmuletNBT::StringTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
return _write_nbt(name, tag, endianness, string_encode);
};
std::string write_nbt(const std::string& name, const AmuletNBT::ListTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
std::string write_nbt(const std::optional<std::string>& name, const AmuletNBT::ListTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
return _write_nbt(name, tag, endianness, string_encode);
};
std::string write_nbt(const std::string& name, const AmuletNBT::CompoundTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
std::string write_nbt(const std::optional<std::string>& name, const AmuletNBT::CompoundTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
return _write_nbt(name, tag, endianness, string_encode);
};
std::string write_nbt(const std::string& name, const AmuletNBT::IntArrayTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
std::string write_nbt(const std::optional<std::string>& name, const AmuletNBT::IntArrayTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
return _write_nbt(name, tag, endianness, string_encode);
};
std::string write_nbt(const std::string& name, const AmuletNBT::LongArrayTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
std::string write_nbt(const std::optional<std::string>& name, const AmuletNBT::LongArrayTag& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
return _write_nbt(name, tag, endianness, string_encode);
};
std::string write_nbt(const std::string& name, const AmuletNBT::TagNode& tag, std::endian endianness, AmuletNBT::StringEncode string_encode){
Expand Down
Loading
Loading