use compound::NbtCompound; use deserializer::NbtReadHelper; use io::Read; use serde::{Deserialize, Serialize}; use serializer::WriteAdaptor; use crate::{ BYTE_ARRAY_ID, BYTE_ID, COMPOUND_ID, DOUBLE_ID, END_ID, Error, FLOAT_ID, INT_ARRAY_ID, INT_ID, LIST_ID, LONG_ARRAY_ID, LONG_ID, MAX_ARRAY_LENGTH, SHORT_ID, STRING_ID, Seek, Write, compound, deserializer, get_nbt_string, io, nbt_byte_array, nbt_int_array, nbt_long_array, serializer, }; #[derive(Clone, Debug, PartialEq)] #[repr(u8)] pub enum NbtTag { End = END_ID, Byte(i8) = BYTE_ID, Short(i16) = SHORT_ID, Int(i32) = INT_ID, Long(i64) = LONG_ID, Float(f32) = FLOAT_ID, Double(f64) = DOUBLE_ID, ByteArray(Vec) = BYTE_ARRAY_ID, String(Box) = STRING_ID, List(Vec) = LIST_ID, Compound(NbtCompound) = COMPOUND_ID, IntArray(Vec) = INT_ARRAY_ID, LongArray(Vec) = LONG_ARRAY_ID, } impl NbtTag { /// Returns the numeric id associated with the data type. #[must_use] pub const fn get_type_id(&self) -> u8 { // Safety: Since Self is repr(u8), it is guaranteed to hold the discriminant in the first byte // See https://doc.rust-lang.org/reference/items/enumerations.html#pointer-casting unsafe { *std::ptr::from_ref::(self).cast::() } } pub fn serialize(self, w: &mut WriteAdaptor) -> serializer::Result<()> { w.write_u8_be(self.get_type_id())?; self.serialize_data(w)?; Ok(()) } pub fn write_string(string: &str, w: &mut WriteAdaptor) -> serializer::Result<()> { let java_string = cesu8::to_java_cesu8(string); let len = java_string.len(); if len > u16::MAX as usize { return Err(Error::LargeLength(len)); } w.write_u16_be(len as u16)?; w.write_slice(&java_string)?; Ok(()) } /// Gets the element type of [`NbtTag::List`] the provided `Vec` /// represents. If any elements in the `Vec` are found to be of /// different types, this returns [`COMPOUND_ID`]. #[must_use] fn get_list_element_type_id(list: &[Self]) -> u8 { let mut element_id = END_ID; for tag in list { let id = tag.get_type_id(); if element_id == END_ID { element_id = id; } else if element_id != id { return COMPOUND_ID; } } element_id } /// Tries to unwrap (flatten) a wrapped `NbtTag`. If there is a wrapped tag, it is returned. /// If no unwrap is possible, this returns the given tag. fn flatten(tag: Self) -> Self { if let Self::Compound(mut compound) = tag { // Try to get the wrapped tag, stored by "". if Self::is_wrapper_compound(&compound) { compound.child_tags.remove("").unwrap() } else { Self::Compound(compound) } } else { tag } } /// Returns whether an [`NbtCompound`] is a wrapper compound. /// /// A *wrapper compound* is a compound that stores exactly one /// key-value pair, an empty string key (`""`) and an `NbtTag`. fn is_wrapper_compound(compound: &NbtCompound) -> bool { compound.child_tags.len() == 1 && compound.child_tags.contains_key("") } /// Wraps the provided tag if needed with the provided element type of list /// the wrapped tag, if any, would be added to. fn wrap_tag_if_needed(element_type: u8, tag: Self) -> Self { if element_type == COMPOUND_ID { if let Self::Compound(compound) = &tag && !Self::is_wrapper_compound(compound) { tag } else { Self::wrap_tag(tag) } } else { tag } } fn wrap_tag(tag: Self) -> Self { let mut compound = NbtCompound::new(); compound.put("", tag); Self::Compound(compound) } pub fn serialize_data(self, w: &mut WriteAdaptor) -> serializer::Result<()> { match self { Self::End => {} Self::Byte(byte) => w.write_i8_be(byte)?, Self::Short(short) => w.write_i16_be(short)?, Self::Int(int) => w.write_i32_be(int)?, Self::Long(long) => w.write_i64_be(long)?, Self::Float(float) => w.write_f32_be(float)?, Self::Double(double) => w.write_f64_be(double)?, Self::ByteArray(byte_array) => { let len = byte_array.len(); if len > i32::MAX as usize { return Err(Error::LargeLength(len)); } w.write_i32_be(len as i32)?; for int in byte_array { w.write_i8_be(int)?; } } Self::String(string) => { Self::write_string(&string, w)?; } Self::List(list) => { let len = list.len(); if len > i32::MAX as usize { return Err(Error::LargeLength(len)); } let list_element_id = Self::get_list_element_type_id(&list); w.write_u8_be(list_element_id)?; w.write_i32_be(len as i32)?; for nbt_tag in list { // Since tags in the same list tag must have the same type, // we need to handle those of different tag types by // wrapping them in `NbtCompound`s if needed. Self::wrap_tag_if_needed(list_element_id, nbt_tag).serialize_data(w)?; } } Self::Compound(compound) => { compound.serialize_content(w)?; } Self::IntArray(int_array) => { let len = int_array.len(); if len > i32::MAX as usize { return Err(Error::LargeLength(len)); } w.write_i32_be(len as i32)?; for int in int_array { w.write_i32_be(int)?; } } Self::LongArray(long_array) => { let len = long_array.len(); if len > i32::MAX as usize { return Err(Error::LargeLength(len)); } w.write_i32_be(len as i32)?; for long in long_array { w.write_i64_be(long)?; } } } Ok(()) } pub fn deserialize(reader: &mut NbtReadHelper) -> Result { let tag_id = reader.get_u8_be()?; Self::deserialize_data(reader, tag_id) } pub fn skip_data( reader: &mut NbtReadHelper, tag_id: u8, ) -> Result<(), Error> { match tag_id { END_ID => Ok(()), BYTE_ID => reader.skip_bytes(1), SHORT_ID => reader.skip_bytes(2), INT_ID | FLOAT_ID => reader.skip_bytes(4), LONG_ID | DOUBLE_ID => reader.skip_bytes(8), BYTE_ARRAY_ID => { let len = reader.get_i32_be()?; if len < 0 { return Err(Error::NegativeLength(len)); } reader.skip_bytes(i64::from(len)) } STRING_ID => { let len = reader.get_u16_be()?; reader.skip_bytes(i64::from(len)) } LIST_ID => { let tag_type_id = reader.get_u8_be()?; let len = reader.get_i32_be()?; if len < 0 { return Err(Error::NegativeLength(len)); } for _ in 0..len { Self::skip_data(reader, tag_type_id)?; } Ok(()) } COMPOUND_ID => NbtCompound::skip_content(reader), INT_ARRAY_ID => { let len = reader.get_i32_be()?; if len < 0 { return Err(Error::NegativeLength(len)); } reader.skip_bytes(i64::from(len) * 4) } LONG_ARRAY_ID => { let len = reader.get_i32_be()?; if len < 0 { return Err(Error::NegativeLength(len)); } reader.skip_bytes(i64::from(len) * 8) } _ => Err(Error::UnknownTagId(tag_id)), } } pub fn deserialize_data( reader: &mut NbtReadHelper, tag_id: u8, ) -> Result { match tag_id { END_ID => Ok(Self::End), BYTE_ID => { let byte = reader.get_i8_be()?; Ok(Self::Byte(byte)) } SHORT_ID => { let short = reader.get_i16_be()?; Ok(Self::Short(short)) } INT_ID => { let int = reader.get_i32_be()?; Ok(Self::Int(int)) } LONG_ID => { let long = reader.get_i64_be()?; Ok(Self::Long(long)) } FLOAT_ID => { let float = reader.get_f32_be()?; Ok(Self::Float(float)) } DOUBLE_ID => { let double = reader.get_f64_be()?; Ok(Self::Double(double)) } BYTE_ARRAY_ID => { let len = reader.get_i32_be()?; if len < 0 { return Err(Error::NegativeLength(len)); } let len = len as usize; if len > MAX_ARRAY_LENGTH { return Err(Error::LargeLength(len)); } let mut byte_array = Vec::with_capacity(len); for _ in 0..len { let byte = reader.get_i8_be()?; byte_array.push(byte); } Ok(Self::ByteArray(byte_array)) } STRING_ID => Ok(Self::String(get_nbt_string(reader)?.into())), LIST_ID => { let tag_type_id = reader.get_u8_be()?; let len = reader.get_i32_be()?; if len < 0 { return Err(Error::NegativeLength(len)); } let len = len as usize; if len > MAX_ARRAY_LENGTH { return Err(Error::LargeLength(len)); } let mut list = Vec::with_capacity(len); for _ in 0..len { let tag = Self::deserialize_data(reader, tag_type_id)?; assert_eq!(tag.get_type_id(), tag_type_id); // Try unwrapping the tag. list.push(Self::flatten(tag)); } Ok(Self::List(list)) } COMPOUND_ID => Ok(Self::Compound(NbtCompound::deserialize_content(reader)?)), INT_ARRAY_ID => { let len = reader.get_i32_be()?; if len < 0 { return Err(Error::NegativeLength(len)); } let len = len as usize; if len > MAX_ARRAY_LENGTH { return Err(Error::LargeLength(len)); } let mut int_array = Vec::with_capacity(len); for _ in 0..len { let int = reader.get_i32_be()?; int_array.push(int); } Ok(Self::IntArray(int_array)) } LONG_ARRAY_ID => { let len = reader.get_i32_be()?; if len < 0 { return Err(Error::NegativeLength(len)); } let len = len as usize; if len > MAX_ARRAY_LENGTH { return Err(Error::LargeLength(len)); } let mut long_array = Vec::with_capacity(len); for _ in 0..len { let long = reader.get_i64_be()?; long_array.push(long); } Ok(Self::LongArray(long_array)) } _ => Err(Error::UnknownTagId(tag_id)), } } #[must_use] pub const fn extract_byte(&self) -> Option { match self { Self::Byte(byte) => Some(*byte), _ => None, } } #[must_use] pub const fn extract_short(&self) -> Option { match self { Self::Short(short) => Some(*short), _ => None, } } #[must_use] pub const fn extract_int(&self) -> Option { match self { Self::Int(int) => Some(*int), _ => None, } } #[must_use] pub const fn extract_long(&self) -> Option { match self { Self::Long(long) => Some(*long), _ => None, } } #[must_use] pub const fn extract_float(&self) -> Option { match self { Self::Float(float) => Some(*float), _ => None, } } #[must_use] pub const fn extract_double(&self) -> Option { match self { Self::Double(double) => Some(*double), _ => None, } } #[must_use] pub fn extract_bool(&self) -> Option { match self { Self::Byte(byte) => Some(byte != &0), _ => None, } } #[must_use] pub fn extract_byte_array(&self) -> Option<&[i8]> { match self { Self::ByteArray(byte_array) => Some(byte_array), _ => None, } } #[must_use] pub fn extract_string(&self) -> Option<&str> { match self { Self::String(string) => Some(string), _ => None, } } #[must_use] pub fn extract_list(&self) -> Option<&[Self]> { match self { Self::List(list) => Some(list), _ => None, } } #[must_use] pub const fn extract_compound(&self) -> Option<&NbtCompound> { match self { Self::Compound(compound) => Some(compound), _ => None, } } #[must_use] pub fn extract_int_array(&self) -> Option<&[i32]> { match self { Self::IntArray(int_array) => Some(int_array), _ => None, } } #[must_use] pub fn extract_long_array(&self) -> Option<&[i64]> { match self { Self::LongArray(long_array) => Some(long_array), _ => None, } } } impl From<&str> for NbtTag { fn from(value: &str) -> Self { Self::String(value.into()) } } impl From<&[i8]> for NbtTag { fn from(value: &[i8]) -> Self { Self::ByteArray(value.to_vec()) } } impl From for NbtTag { fn from(value: f32) -> Self { Self::Float(value) } } impl From for NbtTag { fn from(value: f64) -> Self { Self::Double(value) } } impl From for NbtTag { fn from(value: bool) -> Self { Self::Byte(i8::from(value)) } } impl Serialize for NbtTag { fn serialize(&self, serializer: S) -> Result { match self { Self::End => serializer.serialize_unit(), Self::Byte(v) => serializer.serialize_i8(*v), Self::Short(v) => serializer.serialize_i16(*v), Self::Int(v) => serializer.serialize_i32(*v), Self::Long(v) => serializer.serialize_i64(*v), Self::Float(v) => serializer.serialize_f32(*v), Self::Double(v) => serializer.serialize_f64(*v), Self::ByteArray(v) => nbt_byte_array(v, serializer), Self::String(v) => serializer.serialize_str(v), Self::List(v) => { use serde::ser::SerializeSeq; let mut seq = serializer.serialize_seq(Some(v.len()))?; for item in v { seq.serialize_element(item)?; } seq.end() } Self::Compound(v) => v.serialize(serializer), Self::IntArray(v) => nbt_int_array(v, serializer), Self::LongArray(v) => nbt_long_array(v, serializer), } } } impl<'de> Deserialize<'de> for NbtTag { fn deserialize>(deserializer: D) -> Result { struct NbtTagVisitor; impl<'de> serde::de::Visitor<'de> for NbtTagVisitor { type Value = NbtTag; fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { formatter.write_str("an NBT tag") } fn visit_bool(self, v: bool) -> Result { Ok(NbtTag::Byte(i8::from(v))) } fn visit_i8(self, v: i8) -> Result { Ok(NbtTag::Byte(v)) } fn visit_i16(self, v: i16) -> Result { Ok(NbtTag::Short(v)) } fn visit_i32(self, v: i32) -> Result { Ok(NbtTag::Int(v)) } fn visit_i64(self, v: i64) -> Result { Ok(NbtTag::Long(v)) } fn visit_f32(self, v: f32) -> Result { Ok(NbtTag::Float(v)) } fn visit_f64(self, v: f64) -> Result { Ok(NbtTag::Double(v)) } fn visit_str(self, v: &str) -> Result { Ok(NbtTag::String(v.into())) } fn visit_seq>( self, mut seq: A, ) -> Result { let curr = deserializer::take_curr_visitor_seq_list_id().unwrap_or(LIST_ID); match curr { INT_ARRAY_ID => { let mut vec = Vec::new(); while let Some(value) = seq.next_element()? { vec.push(value); } Ok(NbtTag::IntArray(vec)) } LONG_ARRAY_ID => { let mut vec = Vec::new(); while let Some(value) = seq.next_element()? { vec.push(value); } Ok(NbtTag::LongArray(vec)) } BYTE_ARRAY_ID => { let mut vec = Vec::new(); while let Some(value) = seq.next_element()? { vec.push(value); } Ok(NbtTag::ByteArray(vec)) } _ => { let mut vec = Vec::new(); while let Some(value) = seq.next_element()? { vec.push(value); } Ok(NbtTag::List(vec)) } } } fn visit_map>( self, map: A, ) -> Result { Ok(NbtTag::Compound(NbtCompound::deserialize( serde::de::value::MapAccessDeserializer::new(map), )?)) } } deserializer.deserialize_any(NbtTagVisitor) } }