diff --git a/README.md b/README.md index 5ac7034..28dede2 100644 --- a/README.md +++ b/README.md @@ -4,16 +4,21 @@ 目前进度 -- [ ] 读取 +- [x] 读取 - [x] Java - [x] JavaNetAfter1_20_2 - [x] BedrockDisk - [x] BedrockNetVarInt - [ ] 写入 - - [ ] Java - - [ ] JavaNetAfter1_20_2 + - [x] Java + - [x] JavaNetAfter1_20_2 - [ ] BedrockDisk - [ ] BedrockNetVarInt +- [ ] `Serde` 支持 + - [ ] `Serialize` + - [ ] `Deserialize` + - [ ] `from_value` + - [ ] `to_value` 支持 diff --git a/shen-nbt5/src/lib.rs b/shen-nbt5/src/lib.rs index 5e50b8e..114181a 100644 --- a/shen-nbt5/src/lib.rs +++ b/shen-nbt5/src/lib.rs @@ -1,4 +1,5 @@ pub mod reader; +pub mod writer; use reader::NbtReader; @@ -32,10 +33,21 @@ pub mod nbt_version { fn write_i32_array(writer: &mut Vec, data: &[i32]); fn write_i64_array(writer: &mut Vec, data: &[i64]); fn write_nbt_string(writer: &mut Vec, data: &str); - fn write_list(writer: &mut Vec, data: &[NbtValue]); - fn write_compound(writer: &mut Vec, data: &[(String, NbtValue)]); + fn write_list(writer: &mut Vec, data: &[NbtValue]) -> NbtResult<()>; + fn write_compound( + writer: &mut Vec, + name: Option<&String>, + data: &[(String, NbtValue)], + ) -> NbtResult<()>; - fn to_writer(value: &NbtValue, writer: &mut Vec) -> NbtResult<()>; + fn write_to(value: &NbtValue, buff: &mut Vec) -> NbtResult<()>; + fn write_to_with_name(name: &str, value: &NbtValue, buff: &mut Vec) -> NbtResult<()>; + + fn to_binary(value: &NbtValue) -> NbtResult> { + let mut buff = Vec::new(); + Self::write_to(value, &mut buff)?; + Ok(buff) + } } pub trait NbtReadTrait { @@ -121,6 +133,8 @@ pub enum NbtError { VarIntTooBig(usize), /// Varlong 过大 VarlongTooBig(usize), + /// NbtList 中类型不同 + ListTypeNotSame(Vec), } pub type NbtResult = Result; @@ -129,16 +143,17 @@ impl std::fmt::Display for NbtError { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { match self { NbtError::UnknownErr(s) => write!(f, "未知错误: {}", s), - NbtError::WrongRootType(n) => { - match n { - 9 => { - write!(f, "根节点为 NbtList(9) 类型, 是否应该使用 BedrockDisk/BedrockNetVarInt 解析?") - } - _ => { - write!(f, "根节点类型错误: {}, 应为 NbtCompound/NbtList(bedrock only)", n) - } + NbtError::WrongRootType(n) => match n { + 9 => { + write!( + f, + "根节点为 NbtList(9) 类型, 是否应该使用 BedrockDisk/BedrockNetVarInt?" + ) } - } + _ => { + write!(f, "根节点类型错误: {}, 应为 NbtCompound/NbtList(bedrock only)", n) + } + }, NbtError::RootWithoutName => { write!(f, "根节点无名称, 是否应该使用 JavaNetAfter1_20_2 解析?") } @@ -160,6 +175,9 @@ impl std::fmt::Display for NbtError { ), NbtError::VarIntTooBig(n) => write!(f, "VarInt 过大: {} 最大长度为 5", n), NbtError::VarlongTooBig(n) => write!(f, "VarLong 过大: {} 最大长度为 10", n), + NbtError::ListTypeNotSame(types) => { + write!(f, "NbtList 中类型不同: {:?} 应相同", types) + } } } } @@ -200,11 +218,49 @@ pub enum NbtValue { } impl NbtValue { - pub fn from_binary(data: &mut [u8]) -> NbtResult + pub fn from_binary(data: &mut [u8]) -> NbtResult where - T: nbt_version::NbtReadTrait, + R: nbt_version::NbtReadTrait, { let reader = NbtReader::new(data); - T::from_reader(reader) + R::from_reader(reader) + } + + pub fn tag(&self) -> NbtTypeId { + match self { + NbtValue::Byte(_) => 1, + NbtValue::Short(_) => 2, + NbtValue::Int(_) => 3, + NbtValue::Long(_) => 4, + NbtValue::Float(_) => 5, + NbtValue::Double(_) => 6, + NbtValue::ByteArray(_) => 7, + NbtValue::String(_) => 8, + NbtValue::List(_) => 9, + NbtValue::Compound(_, _) => 10, + NbtValue::IntArray(_) => 11, + NbtValue::LongArray(_) => 12, + } + } + + pub fn write_to(&self, buff: &mut Vec) -> NbtResult<()> + where + W: nbt_version::NbtWriteTrait, + { + W::write_to(self, buff) + } + + pub fn write_to_with_name(&self, name: &str, buff: &mut Vec) -> NbtResult<()> + where + W: nbt_version::NbtWriteTrait, + { + W::write_to_with_name(name, self, buff) + } + + pub fn to_binary(&self) -> NbtResult> + where + W: nbt_version::NbtWriteTrait, + { + W::to_binary(self) } } diff --git a/shen-nbt5/src/reader.rs b/shen-nbt5/src/reader.rs index d1b0c5e..8ec715f 100644 --- a/shen-nbt5/src/reader.rs +++ b/shen-nbt5/src/reader.rs @@ -7,8 +7,6 @@ pub struct NbtReader<'data> { pub data: &'data mut [u8], /// 当前读取的位置 pub cursor: usize, - // be/le - // pub endian: Endian, } /// Java 版 绝大部分的 NBT 格式 diff --git a/shen-nbt5/src/tests.rs b/shen-nbt5/src/tests.rs index 9913cde..81f9214 100644 --- a/shen-nbt5/src/tests.rs +++ b/shen-nbt5/src/tests.rs @@ -298,7 +298,7 @@ mod nbt { Some("hello world".to_string()), vec![("name".to_string(), NbtValue::String("Bananrama".to_string()))], ); - assert_eq!(data, Ok(correct_data)) + assert_eq!(data, Ok(correct_data)); } #[test] @@ -316,6 +316,41 @@ mod nbt { assert_eq!(data, Ok(correct_data)) } + #[test] + fn hello_write() { + let data = NbtValue::Compound( + Some("hello world".to_string()), + vec![("name".to_string(), NbtValue::String("Bananrama".to_string()))], + ); + // 写入 + let mut buff: Vec = Vec::new(); + data.write_to::(&mut buff).unwrap(); + println!("{:?}", buff); + let data: [u8; 0x21] = [ + 0x0A, 0x00, 0x0B, 0x68, 0x65, 0x6C, 0x6C, 0x6F, 0x20, 0x77, 0x6F, 0x72, 0x6C, 0x64, + 0x08, 0x00, 0x04, 0x6E, 0x61, 0x6D, 0x65, 0x00, 0x09, 0x42, 0x61, 0x6E, 0x61, 0x6E, + 0x72, 0x61, 0x6D, 0x61, 0x00, + ]; + assert_eq!(buff, data); + } + + #[test] + fn hello_write_java_net() { + let data = NbtValue::Compound( + None, + vec![("name".to_string(), NbtValue::String("Bananrama".to_string()))], + ); + // 写入 + let mut buff: Vec = Vec::new(); + data.write_to::(&mut buff).unwrap(); + println!("{:?}", buff); + let data: [u8; 20] = [ + 0x0A, 0x08, 0x00, 0x04, 0x6E, 0x61, 0x6D, 0x65, 0x00, 0x09, 0x42, 0x61, 0x6E, 0x61, + 0x6E, 0x72, 0x61, 0x6D, 0x61, 0x00, + ]; + assert_eq!(buff, data); + } + #[test] fn big_test() { let mut data: [u8; 0x608] = [ diff --git a/shen-nbt5/src/writer.rs b/shen-nbt5/src/writer.rs new file mode 100644 index 0000000..e2f9824 --- /dev/null +++ b/shen-nbt5/src/writer.rs @@ -0,0 +1,204 @@ +use crate::nbt_version::{BedrockDisk, BedrockNetVarInt, Java, JavaNetAfter1_20_2, NbtWriteTrait}; +use crate::{NbtError, NbtResult, NbtValue}; + +/// 最简单的一集 +impl NbtWriteTrait for Java { + #[inline] + fn write_i8_array(writer: &mut Vec, data: &[i8]) { + // 写好 tag 了, 直接写入信息 + // 写入长度 + writer.extend_from_slice(&(data.len() as i32).to_be_bytes()); + // 写入数据 + writer.extend_from_slice(data.iter().map(|x| *x as u8).collect::>().as_slice()); + } + #[inline] + fn write_i32_array(writer: &mut Vec, data: &[i32]) { + // 写好 tag 了, 直接写入信息 + // 写入长度 + writer.extend_from_slice(&(data.len() as i32).to_be_bytes()); + // 写入数据 + writer.extend_from_slice( + &data.iter().map(|x| x.to_be_bytes()).collect::>().concat(), + ); + } + #[inline] + fn write_i64_array(writer: &mut Vec, data: &[i64]) { + // 写好 tag 了, 直接写入信息 + // 写入长度 + writer.extend_from_slice(&(data.len() as i32).to_be_bytes()); + // 写入数据 + writer.extend_from_slice( + &data.iter().map(|x| x.to_be_bytes()).collect::>().concat(), + ); + } + #[inline] + fn write_nbt_string(writer: &mut Vec, data: &str) { + // 写入长度 + writer.extend_from_slice(&(data.len() as u16).to_be_bytes()); + // 写入数据 + writer.extend_from_slice(data.as_bytes()); + } + #[inline] + fn write_list(writer: &mut Vec, data: &[NbtValue]) -> NbtResult<()> { + if data.is_empty() { + // 写入一个空的 tag + writer.extend_from_slice(&0i8.to_be_bytes()); + return Ok(()); + } + // 遍历检查一遍所有的 tag 是否一致 + let tag = data.first().unwrap().tag(); + if !data.iter().all(|x| x.tag() == tag) { + return Err(NbtError::ListTypeNotSame(data.iter().map(|x| x.tag()).collect())); + } + // 写入 tag + writer.push(tag); + // 写入长度 + writer.extend_from_slice(&(data.len() as i32).to_be_bytes()); + // 写入数据 + for i in data { + match i { + NbtValue::Byte(x) => writer.push(*x as u8), + NbtValue::Short(x) => writer.extend_from_slice(&x.to_be_bytes()), + NbtValue::Int(x) => writer.extend_from_slice(&x.to_be_bytes()), + NbtValue::Long(x) => writer.extend_from_slice(&x.to_be_bytes()), + NbtValue::Float(x) => writer.extend_from_slice(&x.to_be_bytes()), + NbtValue::Double(x) => writer.extend_from_slice(&x.to_be_bytes()), + NbtValue::ByteArray(x) => Self::write_i8_array(writer, x), + NbtValue::IntArray(x) => Self::write_i32_array(writer, x), + NbtValue::LongArray(x) => Self::write_i64_array(writer, x), + NbtValue::String(x) => Self::write_nbt_string(writer, x), + NbtValue::List(x) => Self::write_list(writer, x)?, + NbtValue::Compound(name, data) => { + Self::write_compound(writer, name.as_ref(), data)? + } + } + } + Ok(()) + } + #[inline] + fn write_compound( + writer: &mut Vec, + name: Option<&String>, + data: &[(String, NbtValue)], + ) -> NbtResult<()> { + // 写入自己的名字 + Self::write_nbt_string(writer, name.unwrap_or(&"".to_string())); + for (key, value) in data { + // 写入 tag + writer.push(value.tag()); + // 写入 key + Self::write_nbt_string(writer, key); + // 写入 value + match value { + NbtValue::Byte(x) => writer.push(*x as u8), + NbtValue::Short(x) => writer.extend_from_slice(&x.to_be_bytes()), + NbtValue::Int(x) => writer.extend_from_slice(&x.to_be_bytes()), + NbtValue::Long(x) => writer.extend_from_slice(&x.to_be_bytes()), + NbtValue::Float(x) => writer.extend_from_slice(&x.to_be_bytes()), + NbtValue::Double(x) => writer.extend_from_slice(&x.to_be_bytes()), + NbtValue::ByteArray(x) => Self::write_i8_array(writer, x), + NbtValue::IntArray(x) => Self::write_i32_array(writer, x), + NbtValue::LongArray(x) => Self::write_i64_array(writer, x), + NbtValue::String(x) => Self::write_nbt_string(writer, x), + NbtValue::List(x) => Self::write_list(writer, x)?, + NbtValue::Compound(name, data) => { + Self::write_compound(writer, name.as_ref(), data)? + } + } + } + // 写入结束 tag + writer.push(0); + Ok(()) + } + fn write_to(value: &NbtValue, buff: &mut Vec) -> NbtResult<()> { + // 写入 tag + match value { + NbtValue::Compound(name, data) => { + buff.push(value.tag()); + Self::write_compound(buff, name.as_ref(), data)? + } + x => return Err(NbtError::WrongRootType(x.tag())), + } + Ok(()) + } + fn write_to_with_name(name: &str, value: &NbtValue, buff: &mut Vec) -> NbtResult<()> { + // 写入 tag + buff.push(value.tag()); + // 写入 key + Self::write_nbt_string(buff, name); + // 写入 value + Self::write_to(value, buff)?; + Ok(()) + } +} + +impl NbtWriteTrait for JavaNetAfter1_20_2 { + #[inline] + fn write_i8_array(writer: &mut Vec, data: &[i8]) { Java::write_i8_array(writer, data); } + #[inline] + fn write_i32_array(writer: &mut Vec, data: &[i32]) { Java::write_i32_array(writer, data); } + #[inline] + fn write_i64_array(writer: &mut Vec, data: &[i64]) { Java::write_i64_array(writer, data); } + #[inline] + fn write_nbt_string(writer: &mut Vec, data: &str) { Java::write_nbt_string(writer, data); } + #[inline] + fn write_list(writer: &mut Vec, data: &[NbtValue]) -> NbtResult<()> { + Java::write_list(writer, data) + } + #[inline] + fn write_compound( + writer: &mut Vec, + name: Option<&String>, + data: &[(String, NbtValue)], + ) -> NbtResult<()> { + Java::write_compound(writer, name, data) + } + #[inline] + fn write_to(value: &NbtValue, buff: &mut Vec) -> NbtResult<()> { + // 写入 tag + match value { + NbtValue::Compound(_, data) => { + // 忽略名字 + buff.push(value.tag()); + for (key, value) in data { + // 写入 tag + buff.push(value.tag()); + // 写入 key + Self::write_nbt_string(buff, key); + // 写入 value + match value { + NbtValue::Byte(x) => buff.push(*x as u8), + NbtValue::Short(x) => buff.extend_from_slice(&x.to_be_bytes()), + NbtValue::Int(x) => buff.extend_from_slice(&x.to_be_bytes()), + NbtValue::Long(x) => buff.extend_from_slice(&x.to_be_bytes()), + NbtValue::Float(x) => buff.extend_from_slice(&x.to_be_bytes()), + NbtValue::Double(x) => buff.extend_from_slice(&x.to_be_bytes()), + NbtValue::ByteArray(x) => Self::write_i8_array(buff, x), + NbtValue::IntArray(x) => Self::write_i32_array(buff, x), + NbtValue::LongArray(x) => Self::write_i64_array(buff, x), + NbtValue::String(x) => Self::write_nbt_string(buff, x), + NbtValue::List(x) => Self::write_list(buff, x)?, + NbtValue::Compound(name, data) => { + Self::write_compound(buff, name.as_ref(), data)? + } + } + } + // 写入结束 tag + buff.push(0); + Ok(()) + } + x => return Err(NbtError::WrongRootType(x.tag())), + } + } + #[inline] + fn write_to_with_name(_name: &str, value: &NbtValue, buff: &mut Vec) -> NbtResult<()> { + // drop name + JavaNetAfter1_20_2::write_to(value, buff) + } + #[inline] + fn to_binary(value: &NbtValue) -> NbtResult> { + let mut buff = Vec::new(); + JavaNetAfter1_20_2::write_to(value, &mut buff)?; + Ok(buff) + } +}