mirror of
https://github.com/azalea-rs/simdnbt.git
synced 2025-08-02 15:36:03 +00:00
fix some bugs
This commit is contained in:
parent
57dfc2c1a9
commit
820cb1f334
11 changed files with 441 additions and 179 deletions
|
@ -6,6 +6,11 @@ pub struct FieldAttrs {
|
||||||
pub flatten: bool,
|
pub flatten: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub struct UnitAttrs {
|
||||||
|
pub rename: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Default, Debug)]
|
#[derive(Default, Debug)]
|
||||||
pub struct StructAttrs {
|
pub struct StructAttrs {
|
||||||
pub deny_unknown_fields: bool,
|
pub deny_unknown_fields: bool,
|
||||||
|
@ -35,6 +40,27 @@ impl Parse for FieldAttrs {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl Parse for UnitAttrs {
|
||||||
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
|
let mut attrs = Self::default();
|
||||||
|
|
||||||
|
while !input.is_empty() {
|
||||||
|
let attr = input.parse::<proc_macro2::Ident>()?;
|
||||||
|
match attr.to_string().as_str() {
|
||||||
|
"rename" => {
|
||||||
|
input.parse::<syn::Token![=]>()?;
|
||||||
|
let rename = input.parse::<syn::LitStr>()?;
|
||||||
|
|
||||||
|
attrs.rename = Some(rename.value());
|
||||||
|
}
|
||||||
|
_ => todo!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(attrs)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Parse for StructAttrs {
|
impl Parse for StructAttrs {
|
||||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||||
let mut attrs = Self::default();
|
let mut attrs = Self::default();
|
||||||
|
@ -63,11 +89,29 @@ pub fn parse_field_attrs(attrs: &[syn::Attribute]) -> FieldAttrs {
|
||||||
if let Some(rename) = new_attr.rename {
|
if let Some(rename) = new_attr.rename {
|
||||||
field_attrs.rename = Some(rename);
|
field_attrs.rename = Some(rename);
|
||||||
}
|
}
|
||||||
|
if new_attr.flatten {
|
||||||
|
field_attrs.flatten = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
field_attrs
|
field_attrs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn parse_unit_attrs(attrs: &[syn::Attribute]) -> UnitAttrs {
|
||||||
|
let mut unit_attrs = UnitAttrs::default();
|
||||||
|
|
||||||
|
for attr in attrs.iter().filter(|attr| attr.path().is_ident("simdnbt")) {
|
||||||
|
let new_attr = attr
|
||||||
|
.parse_args::<UnitAttrs>()
|
||||||
|
.expect("invalid simdnbt attr");
|
||||||
|
if let Some(rename) = new_attr.rename {
|
||||||
|
unit_attrs.rename = Some(rename);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unit_attrs
|
||||||
|
}
|
||||||
|
|
||||||
pub fn parse_struct_attrs(attrs: &[syn::Attribute]) -> StructAttrs {
|
pub fn parse_struct_attrs(attrs: &[syn::Attribute]) -> StructAttrs {
|
||||||
let mut struct_attrs = StructAttrs::default();
|
let mut struct_attrs = StructAttrs::default();
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
mod attrs;
|
mod attrs;
|
||||||
|
|
||||||
use attrs::parse_field_attrs;
|
use attrs::{parse_field_attrs, parse_unit_attrs};
|
||||||
use quote::quote;
|
use quote::quote;
|
||||||
use syn::{parse_macro_input, DeriveInput};
|
use syn::{parse_macro_input, DeriveInput};
|
||||||
|
|
||||||
|
@ -30,10 +30,12 @@ pub fn deserialize_derive(input: proc_macro::TokenStream) -> proc_macro::TokenSt
|
||||||
#struct_field_name: simdnbt::Deserialize::from_compound(nbt)?,
|
#struct_field_name: simdnbt::Deserialize::from_compound(nbt)?,
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
|
let debug_ident = format!("{ident}::{struct_field_name}");
|
||||||
|
|
||||||
field_deserializers.push(quote! {
|
field_deserializers.push(quote! {
|
||||||
#struct_field_name: simdnbt::FromNbtTag::from_optional_nbt_tag(
|
#struct_field_name: simdnbt::FromNbtTag::from_optional_nbt_tag(
|
||||||
nbt.take(#field_name)
|
nbt.take(#field_name)
|
||||||
)?.ok_or(simdnbt::DeserializeError::MismatchedFieldType)?
|
)?.ok_or(simdnbt::DeserializeError::MismatchedFieldType(#debug_ident.to_owned()))?
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,6 +48,8 @@ pub fn deserialize_derive(input: proc_macro::TokenStream) -> proc_macro::TokenSt
|
||||||
}
|
}
|
||||||
|
|
||||||
let generics = input.generics;
|
let generics = input.generics;
|
||||||
|
let where_clause = &generics.where_clause;
|
||||||
|
|
||||||
let struct_attrs = attrs::parse_struct_attrs(&input.attrs);
|
let struct_attrs = attrs::parse_struct_attrs(&input.attrs);
|
||||||
|
|
||||||
let extra_checks = if struct_attrs.deny_unknown_fields {
|
let extra_checks = if struct_attrs.deny_unknown_fields {
|
||||||
|
@ -59,7 +63,7 @@ pub fn deserialize_derive(input: proc_macro::TokenStream) -> proc_macro::TokenSt
|
||||||
};
|
};
|
||||||
|
|
||||||
let output = quote! {
|
let output = quote! {
|
||||||
impl #generics simdnbt::Deserialize for #ident #generics {
|
impl #generics simdnbt::Deserialize for #ident #generics #where_clause {
|
||||||
fn from_compound(mut nbt: simdnbt::owned::NbtCompound) -> Result<Self, simdnbt::DeserializeError> {
|
fn from_compound(mut nbt: simdnbt::owned::NbtCompound) -> Result<Self, simdnbt::DeserializeError> {
|
||||||
let value = Self {
|
let value = Self {
|
||||||
#(#field_deserializers),*
|
#(#field_deserializers),*
|
||||||
|
@ -109,10 +113,10 @@ pub fn serialize_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStre
|
||||||
}
|
}
|
||||||
|
|
||||||
let generics = input.generics;
|
let generics = input.generics;
|
||||||
let struct_attrs = attrs::parse_struct_attrs(&input.attrs);
|
let where_clause = &generics.where_clause;
|
||||||
|
|
||||||
let output = quote! {
|
let output = quote! {
|
||||||
impl #generics simdnbt::Serialize for #ident #generics {
|
impl #generics simdnbt::Serialize for #ident #generics #where_clause {
|
||||||
fn to_compound(self) -> simdnbt::owned::NbtCompound {
|
fn to_compound(self) -> simdnbt::owned::NbtCompound {
|
||||||
let mut nbt = simdnbt::owned::NbtCompound::new();
|
let mut nbt = simdnbt::owned::NbtCompound::new();
|
||||||
#(#field_serializers)*
|
#(#field_serializers)*
|
||||||
|
@ -123,3 +127,106 @@ pub fn serialize_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStre
|
||||||
|
|
||||||
output.into()
|
output.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(FromNbtTag, attributes(simdnbt))]
|
||||||
|
pub fn from_nbt_tag_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
|
let ident = input.ident;
|
||||||
|
|
||||||
|
let mut matchers = Vec::<proc_macro2::TokenStream>::new();
|
||||||
|
|
||||||
|
match input.data {
|
||||||
|
syn::Data::Struct(_) => panic!("Use #[derive(Deserialize)] instead"),
|
||||||
|
syn::Data::Enum(syn::DataEnum { variants, .. }) => {
|
||||||
|
for variant in variants {
|
||||||
|
match variant.fields {
|
||||||
|
syn::Fields::Named(_) => todo!(),
|
||||||
|
syn::Fields::Unnamed(_) => todo!(),
|
||||||
|
syn::Fields::Unit => {
|
||||||
|
let enum_variant_name = variant.ident;
|
||||||
|
|
||||||
|
let mut unit_attrs = parse_unit_attrs(&variant.attrs);
|
||||||
|
|
||||||
|
let variant_name = unit_attrs
|
||||||
|
.rename
|
||||||
|
.take()
|
||||||
|
.unwrap_or_else(|| enum_variant_name.to_string());
|
||||||
|
|
||||||
|
matchers.push(quote! {
|
||||||
|
#variant_name => Some(Self::#enum_variant_name),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
syn::Data::Union(_) => todo!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
let generics = input.generics;
|
||||||
|
let where_clause = &generics.where_clause;
|
||||||
|
|
||||||
|
let output = quote! {
|
||||||
|
impl #generics simdnbt::FromNbtTag for #ident #generics #where_clause {
|
||||||
|
fn from_nbt_tag(tag: simdnbt::owned::NbtTag) -> Option<Self> {
|
||||||
|
match tag.string()?.to_str().as_ref() {
|
||||||
|
#(#matchers)*
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
output.into()
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(ToNbtTag, attributes(simdnbt))]
|
||||||
|
pub fn to_nbt_tag_derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
|
let ident = input.ident;
|
||||||
|
|
||||||
|
let mut field_matchers = Vec::<proc_macro2::TokenStream>::new();
|
||||||
|
|
||||||
|
match input.data {
|
||||||
|
syn::Data::Struct(_) => panic!("Use #[derive(Serialize)] instead"),
|
||||||
|
syn::Data::Enum(syn::DataEnum { variants, .. }) => {
|
||||||
|
for variant in variants {
|
||||||
|
match variant.fields {
|
||||||
|
syn::Fields::Named(_) => todo!(),
|
||||||
|
syn::Fields::Unnamed(_) => todo!(),
|
||||||
|
syn::Fields::Unit => {
|
||||||
|
let enum_variant_name = variant.ident;
|
||||||
|
|
||||||
|
let mut unit_attrs = parse_unit_attrs(&variant.attrs);
|
||||||
|
|
||||||
|
let variant_name = unit_attrs
|
||||||
|
.rename
|
||||||
|
.take()
|
||||||
|
.unwrap_or_else(|| enum_variant_name.to_string());
|
||||||
|
|
||||||
|
field_matchers.push(quote! {
|
||||||
|
Self::#enum_variant_name => simdnbt::owned::NbtTag::String(#variant_name.into()),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
syn::Data::Union(_) => todo!(),
|
||||||
|
}
|
||||||
|
|
||||||
|
let generics = input.generics;
|
||||||
|
let where_clause = &generics.where_clause;
|
||||||
|
|
||||||
|
let output = quote! {
|
||||||
|
impl #generics simdnbt::ToNbtTag for #ident #generics #where_clause {
|
||||||
|
fn to_nbt_tag(self) -> simdnbt::owned::NbtTag {
|
||||||
|
match self {
|
||||||
|
#(#field_matchers)*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
output.into()
|
||||||
|
}
|
||||||
|
|
|
@ -1,10 +1,6 @@
|
||||||
use simdnbt::ToNbtTag;
|
|
||||||
use std::{collections::HashMap, hint::black_box, io::Cursor};
|
use std::{collections::HashMap, hint::black_box, io::Cursor};
|
||||||
|
|
||||||
use simdnbt::{
|
use simdnbt::borrow::{BaseNbt, Nbt};
|
||||||
borrow::{BaseNbt, Nbt},
|
|
||||||
owned,
|
|
||||||
};
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq, Debug)]
|
#[derive(Clone, PartialEq, Debug)]
|
||||||
pub struct Item {
|
pub struct Item {
|
||||||
|
@ -103,44 +99,12 @@ fn items_from_nbt(nbt: BaseNbt) -> Option<Vec<Option<Item>>> {
|
||||||
Some(items)
|
Some(items)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn nbt_from_items(items: Vec<Option<Item>>) -> owned::BaseNbt {
|
|
||||||
owned::BaseNbt::new("", {
|
|
||||||
let i = {
|
|
||||||
let mut i = Vec::new();
|
|
||||||
|
|
||||||
for item in items {
|
|
||||||
let Some(item) = item else {
|
|
||||||
i.push(owned::NbtCompound::new());
|
|
||||||
continue;
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut nbt = {
|
|
||||||
let mut nbt = owned::NbtCompound::new();
|
|
||||||
nbt.insert("id", item.id.to_nbt_tag());
|
|
||||||
nbt.insert("Damage", item.damage.to_nbt_tag());
|
|
||||||
nbt.insert("Count", item.count.to_nbt_tag());
|
|
||||||
nbt
|
|
||||||
};
|
|
||||||
|
|
||||||
i.push(nbt);
|
|
||||||
}
|
|
||||||
|
|
||||||
owned::NbtList::Compound(i)
|
|
||||||
};
|
|
||||||
|
|
||||||
let mut nbt = owned::NbtCompound::new();
|
|
||||||
nbt.insert("i", owned::NbtTag::List(i));
|
|
||||||
nbt
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let input = black_box(include_bytes!("../tests/realworld.nbt"));
|
let input = black_box(include_bytes!("../tests/realworld.nbt"));
|
||||||
|
|
||||||
for _ in 0..1 {
|
for _ in 0..1 {
|
||||||
let nbt = Nbt::read(&mut Cursor::new(input));
|
let nbt = Nbt::read(&mut Cursor::new(input));
|
||||||
let nbt = black_box(nbt.unwrap().unwrap());
|
let nbt = black_box(nbt.unwrap().unwrap());
|
||||||
println!("{:?}", items_from_nbt(nbt));
|
black_box(items_from_nbt(nbt));
|
||||||
// black_box(simdnbt_items_from_nbt(nbt));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -127,13 +127,13 @@ impl<'a> NbtCompound<'a> {
|
||||||
unsafe {
|
unsafe {
|
||||||
unchecked_extend(data, &int_array.len().to_be_bytes());
|
unchecked_extend(data, &int_array.len().to_be_bytes());
|
||||||
}
|
}
|
||||||
data.extend_from_slice(&int_array.as_big_endian());
|
data.extend_from_slice(int_array.as_big_endian());
|
||||||
}
|
}
|
||||||
NbtTag::LongArray(long_array) => {
|
NbtTag::LongArray(long_array) => {
|
||||||
unsafe {
|
unsafe {
|
||||||
unchecked_extend(data, &long_array.len().to_be_bytes());
|
unchecked_extend(data, &long_array.len().to_be_bytes());
|
||||||
}
|
}
|
||||||
data.extend_from_slice(&long_array.as_big_endian());
|
data.extend_from_slice(long_array.as_big_endian());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -113,7 +113,7 @@ pub fn slice_i8_into_u8(s: &[i8]) -> &[u8] {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn write_with_u32_length<'a>(data: &mut Vec<u8>, width: usize, value: &'a [u8]) {
|
pub fn write_with_u32_length(data: &mut Vec<u8>, width: usize, value: &[u8]) {
|
||||||
let length = value.len() / width;
|
let length = value.len() / width;
|
||||||
data.reserve(4 + value.len());
|
data.reserve(4 + value.len());
|
||||||
unsafe {
|
unsafe {
|
||||||
|
@ -171,7 +171,7 @@ pub unsafe fn unchecked_push(data: &mut Vec<u8>, value: u8) {
|
||||||
/// endian! Use [`slice_into_u8_big_endian`] to get big endian (the endianness that's used in NBT).
|
/// endian! Use [`slice_into_u8_big_endian`] to get big endian (the endianness that's used in NBT).
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn slice_into_u8_native_endian<T>(s: &[T]) -> &[u8] {
|
pub fn slice_into_u8_native_endian<T>(s: &[T]) -> &[u8] {
|
||||||
unsafe { slice::from_raw_parts(s.as_ptr() as *const u8, s.len() * std::mem::size_of::<T>()) }
|
unsafe { slice::from_raw_parts(s.as_ptr() as *const u8, std::mem::size_of_val(s)) }
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convert a slice of any type into a Vec<u8>. This will return the data as big endian (the
|
/// Convert a slice of any type into a Vec<u8>. This will return the data as big endian (the
|
||||||
|
|
|
@ -18,8 +18,8 @@ pub enum Error {
|
||||||
pub enum DeserializeError {
|
pub enum DeserializeError {
|
||||||
#[error("Missing field")]
|
#[error("Missing field")]
|
||||||
MissingField,
|
MissingField,
|
||||||
#[error("Mismatched type")]
|
#[error("Mismatched type for {0}")]
|
||||||
MismatchedFieldType,
|
MismatchedFieldType(String),
|
||||||
#[error("Unknown fields {0:?}")]
|
#[error("Unknown fields {0:?}")]
|
||||||
UnknownField(Vec<String>),
|
UnknownField(Vec<String>),
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,12 +8,12 @@ use std::{
|
||||||
};
|
};
|
||||||
|
|
||||||
/// A M-UTF8 string slice. This is how strings are represented internally in NBT.
|
/// A M-UTF8 string slice. This is how strings are represented internally in NBT.
|
||||||
#[derive(Debug, Eq, PartialEq)]
|
#[derive(Eq, PartialEq)]
|
||||||
pub struct Mutf8Str {
|
pub struct Mutf8Str {
|
||||||
pub(crate) slice: [u8],
|
pub(crate) slice: [u8],
|
||||||
}
|
}
|
||||||
/// An owned M-UTF8 string.
|
/// An owned M-UTF8 string.
|
||||||
#[derive(Debug, Eq, PartialEq, Clone, Default)]
|
#[derive(Eq, PartialEq, Clone, Default)]
|
||||||
pub struct Mutf8String {
|
pub struct Mutf8String {
|
||||||
pub(crate) vec: Vec<u8>,
|
pub(crate) vec: Vec<u8>,
|
||||||
}
|
}
|
||||||
|
@ -116,6 +116,10 @@ impl Mutf8Str {
|
||||||
self.slice.len()
|
self.slice.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.slice.is_empty()
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn as_bytes(&self) -> &[u8] {
|
pub fn as_bytes(&self) -> &[u8] {
|
||||||
&self.slice
|
&self.slice
|
||||||
|
@ -128,6 +132,18 @@ impl fmt::Display for Mutf8Str {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Mutf8Str {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_tuple("Mutf8Str").field(&self.to_str()).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Debug for Mutf8String {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
f.debug_tuple("Mutf8String").field(&self.to_str()).finish()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl ToOwned for Mutf8Str {
|
impl ToOwned for Mutf8Str {
|
||||||
type Owned = Mutf8String;
|
type Owned = Mutf8String;
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,9 @@
|
||||||
use std::{io::Cursor, mem};
|
use std::{io::Cursor, mem};
|
||||||
|
|
||||||
use byteorder::{ReadBytesExt, BE};
|
use byteorder::ReadBytesExt;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{
|
common::{read_string, unchecked_push, unchecked_write_string, END_ID, MAX_DEPTH},
|
||||||
read_int_array, read_long_array, read_string, read_with_u32_length,
|
|
||||||
slice_into_u8_big_endian, unchecked_extend, unchecked_push, unchecked_write_string,
|
|
||||||
write_string, BYTE_ARRAY_ID, BYTE_ID, COMPOUND_ID, DOUBLE_ID, END_ID, FLOAT_ID,
|
|
||||||
INT_ARRAY_ID, INT_ID, LIST_ID, LONG_ARRAY_ID, LONG_ID, MAX_DEPTH, SHORT_ID, STRING_ID,
|
|
||||||
},
|
|
||||||
mutf8::Mutf8String,
|
mutf8::Mutf8String,
|
||||||
Error, Mutf8Str,
|
Error, Mutf8Str,
|
||||||
};
|
};
|
||||||
|
@ -46,49 +41,7 @@ impl NbtCompound {
|
||||||
}
|
}
|
||||||
let tag_name = read_string(data)?.to_owned();
|
let tag_name = read_string(data)?.to_owned();
|
||||||
|
|
||||||
match tag_type {
|
values.push((tag_name, NbtTag::read_with_type(data, tag_type, depth)?));
|
||||||
BYTE_ID => values.push((
|
|
||||||
tag_name,
|
|
||||||
NbtTag::Byte(data.read_i8().map_err(|_| Error::UnexpectedEof)?),
|
|
||||||
)),
|
|
||||||
SHORT_ID => values.push((
|
|
||||||
tag_name,
|
|
||||||
NbtTag::Short(data.read_i16::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
|
||||||
)),
|
|
||||||
INT_ID => values.push((
|
|
||||||
tag_name,
|
|
||||||
NbtTag::Int(data.read_i32::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
|
||||||
)),
|
|
||||||
LONG_ID => values.push((
|
|
||||||
tag_name,
|
|
||||||
NbtTag::Long(data.read_i64::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
|
||||||
)),
|
|
||||||
FLOAT_ID => values.push((
|
|
||||||
tag_name,
|
|
||||||
NbtTag::Float(data.read_f32::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
|
||||||
)),
|
|
||||||
DOUBLE_ID => values.push((
|
|
||||||
tag_name,
|
|
||||||
NbtTag::Double(data.read_f64::<BE>().map_err(|_| Error::UnexpectedEof)?),
|
|
||||||
)),
|
|
||||||
BYTE_ARRAY_ID => values.push((
|
|
||||||
tag_name,
|
|
||||||
NbtTag::ByteArray(read_with_u32_length(data, 1)?.to_owned()),
|
|
||||||
)),
|
|
||||||
STRING_ID => values.push((tag_name, NbtTag::String(read_string(data)?.to_owned()))),
|
|
||||||
LIST_ID => values.push((tag_name, NbtTag::List(NbtList::read(data, depth + 1)?))),
|
|
||||||
COMPOUND_ID => values.push((
|
|
||||||
tag_name,
|
|
||||||
NbtTag::Compound(NbtCompound::read_with_depth(data, depth + 1)?),
|
|
||||||
)),
|
|
||||||
INT_ARRAY_ID => {
|
|
||||||
values.push((tag_name, NbtTag::IntArray(read_int_array(data)?.to_vec())))
|
|
||||||
}
|
|
||||||
LONG_ARRAY_ID => {
|
|
||||||
values.push((tag_name, NbtTag::LongArray(read_long_array(data)?.to_vec())))
|
|
||||||
}
|
|
||||||
_ => return Err(Error::UnknownTagId(tag_type)),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Ok(Self { values })
|
Ok(Self { values })
|
||||||
}
|
}
|
||||||
|
@ -102,53 +55,7 @@ impl NbtCompound {
|
||||||
unsafe {
|
unsafe {
|
||||||
unchecked_push(data, tag.id());
|
unchecked_push(data, tag.id());
|
||||||
unchecked_write_string(data, name);
|
unchecked_write_string(data, name);
|
||||||
}
|
tag.unchecked_write_without_tag_type(data);
|
||||||
match tag {
|
|
||||||
NbtTag::Byte(byte) => unsafe {
|
|
||||||
unchecked_push(data, *byte as u8);
|
|
||||||
},
|
|
||||||
NbtTag::Short(short) => unsafe {
|
|
||||||
unchecked_extend(data, &short.to_be_bytes());
|
|
||||||
},
|
|
||||||
NbtTag::Int(int) => unsafe {
|
|
||||||
unchecked_extend(data, &int.to_be_bytes());
|
|
||||||
},
|
|
||||||
NbtTag::Long(long) => {
|
|
||||||
data.extend_from_slice(&long.to_be_bytes());
|
|
||||||
}
|
|
||||||
NbtTag::Float(float) => unsafe {
|
|
||||||
unchecked_extend(data, &float.to_be_bytes());
|
|
||||||
},
|
|
||||||
NbtTag::Double(double) => {
|
|
||||||
data.extend_from_slice(&double.to_be_bytes());
|
|
||||||
}
|
|
||||||
NbtTag::ByteArray(byte_array) => {
|
|
||||||
unsafe {
|
|
||||||
unchecked_extend(data, &byte_array.len().to_be_bytes());
|
|
||||||
}
|
|
||||||
data.extend_from_slice(byte_array);
|
|
||||||
}
|
|
||||||
NbtTag::String(string) => {
|
|
||||||
write_string(data, string);
|
|
||||||
}
|
|
||||||
NbtTag::List(list) => {
|
|
||||||
list.write(data);
|
|
||||||
}
|
|
||||||
NbtTag::Compound(compound) => {
|
|
||||||
compound.write(data);
|
|
||||||
}
|
|
||||||
NbtTag::IntArray(int_array) => {
|
|
||||||
unsafe {
|
|
||||||
unchecked_extend(data, &int_array.len().to_be_bytes());
|
|
||||||
}
|
|
||||||
data.extend_from_slice(&slice_into_u8_big_endian(int_array));
|
|
||||||
}
|
|
||||||
NbtTag::LongArray(long_array) => {
|
|
||||||
unsafe {
|
|
||||||
unchecked_extend(data, &long_array.len().to_be_bytes());
|
|
||||||
}
|
|
||||||
data.extend_from_slice(&slice_into_u8_big_endian(long_array));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
data.push(END_ID);
|
data.push(END_ID);
|
||||||
|
@ -301,9 +208,6 @@ impl NbtCompound {
|
||||||
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut NbtTag> {
|
pub fn values_mut(&mut self) -> impl Iterator<Item = &mut NbtTag> {
|
||||||
self.values.iter_mut().map(|(_, v)| v)
|
self.values.iter_mut().map(|(_, v)| v)
|
||||||
}
|
}
|
||||||
pub fn into_iter(self) -> impl Iterator<Item = (Mutf8String, NbtTag)> {
|
|
||||||
self.values.into_iter()
|
|
||||||
}
|
|
||||||
pub fn clear(&mut self) {
|
pub fn clear(&mut self) {
|
||||||
self.values.clear();
|
self.values.clear();
|
||||||
}
|
}
|
||||||
|
@ -322,3 +226,12 @@ impl NbtCompound {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IntoIterator for NbtCompound {
|
||||||
|
type Item = (Mutf8String, NbtTag);
|
||||||
|
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.values.into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,13 +5,14 @@ mod list;
|
||||||
|
|
||||||
use std::{io::Cursor, ops::Deref};
|
use std::{io::Cursor, ops::Deref};
|
||||||
|
|
||||||
use byteorder::ReadBytesExt;
|
use byteorder::{ReadBytesExt, BE};
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
common::{
|
common::{
|
||||||
read_string, read_u32, write_string, BYTE_ARRAY_ID, BYTE_ID, COMPOUND_ID, DOUBLE_ID,
|
read_int_array, read_long_array, read_string, read_u32, read_with_u32_length,
|
||||||
END_ID, FLOAT_ID, INT_ARRAY_ID, INT_ID, LIST_ID, LONG_ARRAY_ID, LONG_ID, MAX_DEPTH,
|
slice_into_u8_big_endian, unchecked_extend, unchecked_push, write_string, BYTE_ARRAY_ID,
|
||||||
SHORT_ID, STRING_ID,
|
BYTE_ID, COMPOUND_ID, DOUBLE_ID, END_ID, FLOAT_ID, INT_ARRAY_ID, INT_ID, LIST_ID,
|
||||||
|
LONG_ARRAY_ID, LONG_ID, MAX_DEPTH, SHORT_ID, STRING_ID,
|
||||||
},
|
},
|
||||||
mutf8::Mutf8String,
|
mutf8::Mutf8String,
|
||||||
Error, Mutf8Str,
|
Error, Mutf8Str,
|
||||||
|
@ -52,6 +53,22 @@ impl Nbt {
|
||||||
Ok(Nbt::Some(BaseNbt { name, tag }))
|
Ok(Nbt::Some(BaseNbt { name, tag }))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn read_unnamed(data: &mut Cursor<&[u8]>) -> Result<Nbt, Error> {
|
||||||
|
let root_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
|
||||||
|
if root_type == END_ID {
|
||||||
|
return Ok(Nbt::None);
|
||||||
|
}
|
||||||
|
if root_type != COMPOUND_ID {
|
||||||
|
return Err(Error::InvalidRootType(root_type));
|
||||||
|
}
|
||||||
|
let tag = NbtCompound::read_with_depth(data, 0)?;
|
||||||
|
|
||||||
|
Ok(Nbt::Some(BaseNbt {
|
||||||
|
name: Mutf8String::from(""),
|
||||||
|
tag,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
pub fn write(&self, data: &mut Vec<u8>) {
|
pub fn write(&self, data: &mut Vec<u8>) {
|
||||||
match self {
|
match self {
|
||||||
Nbt::Some(nbt) => nbt.write(data),
|
Nbt::Some(nbt) => nbt.write(data),
|
||||||
|
@ -61,6 +78,15 @@ impl Nbt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn write_unnamed(&self, data: &mut Vec<u8>) {
|
||||||
|
match self {
|
||||||
|
Nbt::Some(nbt) => nbt.write_unnamed(data),
|
||||||
|
Nbt::None => {
|
||||||
|
data.push(END_ID);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn unwrap(self) -> BaseNbt {
|
pub fn unwrap(self) -> BaseNbt {
|
||||||
match self {
|
match self {
|
||||||
Nbt::Some(nbt) => nbt,
|
Nbt::Some(nbt) => nbt,
|
||||||
|
@ -68,6 +94,13 @@ impl Nbt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn unwrap_or<'a>(&'a self, default: &'a BaseNbt) -> &'a BaseNbt {
|
||||||
|
match self {
|
||||||
|
Nbt::Some(nbt) => nbt,
|
||||||
|
Nbt::None => default,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_some(&self) -> bool {
|
pub fn is_some(&self) -> bool {
|
||||||
match self {
|
match self {
|
||||||
Nbt::Some(_) => true,
|
Nbt::Some(_) => true,
|
||||||
|
@ -80,7 +113,7 @@ impl Nbt {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn iter(&self) -> impl Iterator<Item = (&Mutf8Str, &NbtTag)> {
|
pub fn iter(&self) -> impl Iterator<Item = (&Mutf8Str, &NbtTag)> {
|
||||||
const EMPTY: &'static NbtCompound = &NbtCompound { values: Vec::new() };
|
const EMPTY: &NbtCompound = &NbtCompound { values: Vec::new() };
|
||||||
|
|
||||||
if let Nbt::Some(nbt) = self {
|
if let Nbt::Some(nbt) = self {
|
||||||
nbt.iter()
|
nbt.iter()
|
||||||
|
@ -88,21 +121,12 @@ impl Nbt {
|
||||||
EMPTY.iter()
|
EMPTY.iter()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_iter(self) -> impl Iterator<Item = (Mutf8String, NbtTag)> {
|
|
||||||
const EMPTY: NbtCompound = NbtCompound { values: Vec::new() };
|
|
||||||
|
|
||||||
match self {
|
|
||||||
Nbt::Some(nbt) => nbt.tag.into_iter(),
|
|
||||||
Nbt::None => EMPTY.into_iter(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
impl Deref for Nbt {
|
impl Deref for Nbt {
|
||||||
type Target = BaseNbt;
|
type Target = BaseNbt;
|
||||||
|
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
const EMPTY: &'static BaseNbt = &BaseNbt {
|
const EMPTY: &BaseNbt = &BaseNbt {
|
||||||
name: Mutf8String { vec: Vec::new() },
|
name: Mutf8String { vec: Vec::new() },
|
||||||
tag: NbtCompound { values: Vec::new() },
|
tag: NbtCompound { values: Vec::new() },
|
||||||
};
|
};
|
||||||
|
@ -114,6 +138,20 @@ impl Deref for Nbt {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IntoIterator for Nbt {
|
||||||
|
type Item = (Mutf8String, NbtTag);
|
||||||
|
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
const EMPTY: NbtCompound = NbtCompound { values: Vec::new() };
|
||||||
|
|
||||||
|
match self {
|
||||||
|
Nbt::Some(nbt) => nbt.tag.into_iter(),
|
||||||
|
Nbt::None => EMPTY.into_iter(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BaseNbt {
|
impl BaseNbt {
|
||||||
pub fn new(name: impl Into<Mutf8String>, tag: NbtCompound) -> Self {
|
pub fn new(name: impl Into<Mutf8String>, tag: NbtCompound) -> Self {
|
||||||
let name = name.into();
|
let name = name.into();
|
||||||
|
@ -132,14 +170,25 @@ impl BaseNbt {
|
||||||
self.tag.write(data);
|
self.tag.write(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_iter(self) -> impl Iterator<Item = (Mutf8String, NbtTag)> {
|
pub fn write_unnamed(&self, data: &mut Vec<u8>) {
|
||||||
self.tag.into_iter()
|
data.push(COMPOUND_ID);
|
||||||
|
self.tag.write(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn into_inner(self) -> NbtCompound {
|
pub fn into_inner(self) -> NbtCompound {
|
||||||
self.tag
|
self.tag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IntoIterator for BaseNbt {
|
||||||
|
type Item = (Mutf8String, NbtTag);
|
||||||
|
type IntoIter = std::vec::IntoIter<Self::Item>;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
self.tag.into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl Deref for BaseNbt {
|
impl Deref for BaseNbt {
|
||||||
type Target = NbtCompound;
|
type Target = NbtCompound;
|
||||||
|
|
||||||
|
@ -176,6 +225,109 @@ impl NbtTag {
|
||||||
unsafe { *<*const _>::from(self).cast::<u8>() }
|
unsafe { *<*const _>::from(self).cast::<u8>() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn read_with_type(data: &mut Cursor<&[u8]>, tag_type: u8, depth: usize) -> Result<Self, Error> {
|
||||||
|
match tag_type {
|
||||||
|
BYTE_ID => Ok(NbtTag::Byte(
|
||||||
|
data.read_i8().map_err(|_| Error::UnexpectedEof)?,
|
||||||
|
)),
|
||||||
|
SHORT_ID => Ok(NbtTag::Short(
|
||||||
|
data.read_i16::<BE>().map_err(|_| Error::UnexpectedEof)?,
|
||||||
|
)),
|
||||||
|
INT_ID => Ok(NbtTag::Int(
|
||||||
|
data.read_i32::<BE>().map_err(|_| Error::UnexpectedEof)?,
|
||||||
|
)),
|
||||||
|
LONG_ID => Ok(NbtTag::Long(
|
||||||
|
data.read_i64::<BE>().map_err(|_| Error::UnexpectedEof)?,
|
||||||
|
)),
|
||||||
|
FLOAT_ID => Ok(NbtTag::Float(
|
||||||
|
data.read_f32::<BE>().map_err(|_| Error::UnexpectedEof)?,
|
||||||
|
)),
|
||||||
|
DOUBLE_ID => Ok(NbtTag::Double(
|
||||||
|
data.read_f64::<BE>().map_err(|_| Error::UnexpectedEof)?,
|
||||||
|
)),
|
||||||
|
BYTE_ARRAY_ID => Ok(NbtTag::ByteArray(read_with_u32_length(data, 1)?.to_owned())),
|
||||||
|
STRING_ID => Ok(NbtTag::String(read_string(data)?.to_owned())),
|
||||||
|
LIST_ID => Ok(NbtTag::List(NbtList::read(data, depth + 1)?)),
|
||||||
|
COMPOUND_ID => Ok(NbtTag::Compound(NbtCompound::read_with_depth(
|
||||||
|
data,
|
||||||
|
depth + 1,
|
||||||
|
)?)),
|
||||||
|
INT_ARRAY_ID => Ok(NbtTag::IntArray(read_int_array(data)?.to_vec())),
|
||||||
|
LONG_ARRAY_ID => Ok(NbtTag::LongArray(read_long_array(data)?.to_vec())),
|
||||||
|
_ => Err(Error::UnknownTagId(tag_type)),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn read(data: &mut Cursor<&[u8]>) -> Result<Self, Error> {
|
||||||
|
let tag_type = data.read_u8().map_err(|_| Error::UnexpectedEof)?;
|
||||||
|
Self::read_with_type(data, tag_type, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Write to the data without checking that there's enough space in it.
|
||||||
|
///
|
||||||
|
/// # Safety
|
||||||
|
///
|
||||||
|
/// This function is unsafe because it doesn't check that there's enough space in the data.
|
||||||
|
/// 4 bytes MUST be reserved before calling this function.
|
||||||
|
unsafe fn unchecked_write_without_tag_type(&self, data: &mut Vec<u8>) {
|
||||||
|
match self {
|
||||||
|
NbtTag::Byte(byte) => unsafe {
|
||||||
|
unchecked_push(data, *byte as u8);
|
||||||
|
},
|
||||||
|
NbtTag::Short(short) => unsafe {
|
||||||
|
unchecked_extend(data, &short.to_be_bytes());
|
||||||
|
},
|
||||||
|
NbtTag::Int(int) => unsafe {
|
||||||
|
unchecked_extend(data, &int.to_be_bytes());
|
||||||
|
},
|
||||||
|
NbtTag::Long(long) => {
|
||||||
|
data.extend_from_slice(&long.to_be_bytes());
|
||||||
|
}
|
||||||
|
NbtTag::Float(float) => unsafe {
|
||||||
|
unchecked_extend(data, &float.to_be_bytes());
|
||||||
|
},
|
||||||
|
NbtTag::Double(double) => {
|
||||||
|
data.extend_from_slice(&double.to_be_bytes());
|
||||||
|
}
|
||||||
|
NbtTag::ByteArray(byte_array) => {
|
||||||
|
unsafe {
|
||||||
|
unchecked_extend(data, &byte_array.len().to_be_bytes());
|
||||||
|
}
|
||||||
|
data.extend_from_slice(byte_array);
|
||||||
|
}
|
||||||
|
NbtTag::String(string) => {
|
||||||
|
write_string(data, string);
|
||||||
|
}
|
||||||
|
NbtTag::List(list) => {
|
||||||
|
list.write(data);
|
||||||
|
}
|
||||||
|
NbtTag::Compound(compound) => {
|
||||||
|
compound.write(data);
|
||||||
|
}
|
||||||
|
NbtTag::IntArray(int_array) => {
|
||||||
|
unsafe {
|
||||||
|
unchecked_extend(data, &int_array.len().to_be_bytes());
|
||||||
|
}
|
||||||
|
data.extend_from_slice(&slice_into_u8_big_endian(int_array));
|
||||||
|
}
|
||||||
|
NbtTag::LongArray(long_array) => {
|
||||||
|
unsafe {
|
||||||
|
unchecked_extend(data, &long_array.len().to_be_bytes());
|
||||||
|
}
|
||||||
|
data.extend_from_slice(&slice_into_u8_big_endian(long_array));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn write(&self, data: &mut Vec<u8>) {
|
||||||
|
data.reserve(1 + 4);
|
||||||
|
// SAFETY: We just reserved enough space for the tag ID and 4 bytes of tag data.
|
||||||
|
unsafe {
|
||||||
|
unchecked_push(data, self.id());
|
||||||
|
self.unchecked_write_without_tag_type(data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn byte(&self) -> Option<i8> {
|
pub fn byte(&self) -> Option<i8> {
|
||||||
match self {
|
match self {
|
||||||
NbtTag::Byte(byte) => Some(*byte),
|
NbtTag::Byte(byte) => Some(*byte),
|
||||||
|
|
|
@ -20,6 +20,10 @@ impl<'a, T> RawList<'a, T> {
|
||||||
pub fn len(&self) -> usize {
|
pub fn len(&self) -> usize {
|
||||||
self.data.len() / std::mem::size_of::<T>()
|
self.data.len() / std::mem::size_of::<T>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_empty(&self) -> bool {
|
||||||
|
self.data.is_empty()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<T: SwappableNumber> RawList<'_, T> {
|
impl<T: SwappableNumber> RawList<'_, T> {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
use std::{collections::HashMap, fmt::Display, hash::Hash, hash::Hasher, str::FromStr};
|
use std::{collections::HashMap, fmt::Display, hash::Hash, str::FromStr};
|
||||||
|
|
||||||
use crate::DeserializeError;
|
use crate::DeserializeError;
|
||||||
|
|
||||||
|
@ -42,12 +42,16 @@ impl<K: Display + FromStr + Eq + Hash, V: FromNbtTag> Deserialize for HashMap<K,
|
||||||
let mut hashmap = HashMap::with_capacity(compound.values.len());
|
let mut hashmap = HashMap::with_capacity(compound.values.len());
|
||||||
|
|
||||||
for (k, v) in compound.values {
|
for (k, v) in compound.values {
|
||||||
hashmap.insert(
|
let k_str = k.to_str();
|
||||||
k.to_str()
|
let k_parsed = k_str
|
||||||
.parse()
|
.parse()
|
||||||
.map_err(|_| DeserializeError::MismatchedFieldType)?,
|
.map_err(|_| DeserializeError::MismatchedFieldType("key".to_owned()))?;
|
||||||
V::from_nbt_tag(v).ok_or(DeserializeError::MismatchedFieldType)?,
|
|
||||||
);
|
let v_parsed = V::from_nbt_tag(v).ok_or_else(|| {
|
||||||
|
DeserializeError::MismatchedFieldType(format!("value for key {k_str}"))
|
||||||
|
})?;
|
||||||
|
|
||||||
|
hashmap.insert(k_parsed, v_parsed);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(hashmap)
|
Ok(hashmap)
|
||||||
|
@ -89,6 +93,17 @@ impl<T: Serialize> ToNbtTag for T {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl FromNbtTag for crate::owned::NbtTag {
|
||||||
|
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
|
||||||
|
Some(tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ToNbtTag for crate::owned::NbtTag {
|
||||||
|
fn to_nbt_tag(self) -> crate::owned::NbtTag {
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// standard nbt types
|
// standard nbt types
|
||||||
impl FromNbtTag for i8 {
|
impl FromNbtTag for i8 {
|
||||||
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
|
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
|
||||||
|
@ -167,6 +182,51 @@ impl ToNbtTag for String {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// unsigned integers
|
||||||
|
impl FromNbtTag for u8 {
|
||||||
|
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
|
||||||
|
tag.byte().map(|b| b as u8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ToNbtTag for u8 {
|
||||||
|
fn to_nbt_tag(self) -> crate::owned::NbtTag {
|
||||||
|
crate::owned::NbtTag::Byte(self as i8)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromNbtTag for u16 {
|
||||||
|
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
|
||||||
|
tag.short().map(|s| s as u16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ToNbtTag for u16 {
|
||||||
|
fn to_nbt_tag(self) -> crate::owned::NbtTag {
|
||||||
|
crate::owned::NbtTag::Short(self as i16)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromNbtTag for u32 {
|
||||||
|
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
|
||||||
|
tag.int().map(|i| i as u32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ToNbtTag for u32 {
|
||||||
|
fn to_nbt_tag(self) -> crate::owned::NbtTag {
|
||||||
|
crate::owned::NbtTag::Int(self as i32)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromNbtTag for u64 {
|
||||||
|
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
|
||||||
|
tag.long().map(|l| l as u64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
impl ToNbtTag for u64 {
|
||||||
|
fn to_nbt_tag(self) -> crate::owned::NbtTag {
|
||||||
|
crate::owned::NbtTag::Long(self as i64)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// lists
|
// lists
|
||||||
impl FromNbtTag for Vec<String> {
|
impl FromNbtTag for Vec<String> {
|
||||||
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
|
fn from_nbt_tag(tag: crate::owned::NbtTag) -> Option<Self> {
|
||||||
|
@ -203,10 +263,7 @@ impl<T: ToNbtTag> ToNbtTag for Option<T> {
|
||||||
panic!("Called to_nbt_tag on Option<T>. Use to_optional_nbt_tag instead.")
|
panic!("Called to_nbt_tag on Option<T>. Use to_optional_nbt_tag instead.")
|
||||||
}
|
}
|
||||||
fn to_optional_nbt_tag(self) -> Option<crate::owned::NbtTag> {
|
fn to_optional_nbt_tag(self) -> Option<crate::owned::NbtTag> {
|
||||||
match self {
|
self.map(|t| t.to_nbt_tag())
|
||||||
Some(t) => Some(t.to_nbt_tag()),
|
|
||||||
None => None,
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -263,3 +320,8 @@ impl FromNbtTag for bool {
|
||||||
tag.byte().map(|b| b != 0)
|
tag.byte().map(|b| b != 0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
impl ToNbtTag for bool {
|
||||||
|
fn to_nbt_tag(self) -> crate::owned::NbtTag {
|
||||||
|
crate::owned::NbtTag::Byte(if self { 1 } else { 0 })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue