From e3fa34e89cc12a7e4bd7ae2b908e3ff69acf2349 Mon Sep 17 00:00:00 2001 From: shenjack <3695888@qq.com> Date: Sun, 10 Mar 2024 12:53:21 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0NBT=E8=A7=A3=E6=9E=90?= =?UTF-8?q?=E5=99=A8=E8=BF=9B=E5=BA=A6=E5=92=8C=E9=94=99=E8=AF=AF=E5=A4=84?= =?UTF-8?q?=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 13 +++ shen-nbt5/src/lib.rs | 6 ++ shen-nbt5/src/reader.rs | 193 +++++++++++++++++++++++++++++++++++++--- 3 files changed, 202 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 1a4f094..9ff9a18 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,19 @@ 一个 "全功能" 的 "快速" NBT 解析器 +目前进度 + +- [ ] 读取 + - [x] Java + - [x] JavaNetAfter1_20_2 + - [x] BedrockDisk + - [ ] BedrockNetVarInt +- [ ] 写入 + - [ ] Java + - [ ] JavaNetAfter1_20_2 + - [ ] BedrockDisk + - [ ] BedrockNetVarInt + 支持 - `Java` diff --git a/shen-nbt5/src/lib.rs b/shen-nbt5/src/lib.rs index ed023bc..bffa2d6 100644 --- a/shen-nbt5/src/lib.rs +++ b/shen-nbt5/src/lib.rs @@ -106,6 +106,10 @@ pub enum NbtError { /// - 数据长度 /// - 数据总长度 CursorOverflow(usize, usize, usize), + /// Varint 过大 + VarIntTooBig(usize), + /// Varlong 过大 + VarlongTooBig(usize), } pub type NbtResult = Result; @@ -143,6 +147,8 @@ impl std::fmt::Display for NbtError { cursor + len, data_len ), + NbtError::VarIntTooBig(n) => write!(f, "VarInt 过大: {} 最大长度为 5", n), + NbtError::VarlongTooBig(n) => write!(f, "VarLong 过大: {} 最大长度为 10", n), } } } diff --git a/shen-nbt5/src/reader.rs b/shen-nbt5/src/reader.rs index 50ad5cc..f9d2100 100644 --- a/shen-nbt5/src/reader.rs +++ b/shen-nbt5/src/reader.rs @@ -107,7 +107,7 @@ impl nbt_version::NbtReadTrait for nbt_version::Java { } /// 两个最好实现的就在这里了 -/// +/// /// 网络 NBT: 1.20.2+ 的网络 NBT 根节点没有名字 impl NbtReadTrait for JavaNetAfter1_20_2 { #[inline] @@ -146,13 +146,144 @@ impl NbtReadTrait for JavaNetAfter1_20_2 { /// 基岩版的其实也还行, 就是有点麻烦 /// /// 所有都是小端 -// impl NbtReadTrait for BedrockDisk { -// #[inline] -// fn read_nbt_string(reader: &mut NbtReader) -> NbtResult { -// let len = reader.read_le_u16() as usize; -// reader.read_string(len) -// } -// } +impl NbtReadTrait for BedrockDisk { + #[inline] + fn read_nbt_string(reader: &mut NbtReader) -> NbtResult { + let len = reader.read_le_u16() as usize; + reader.read_string(len) + } + #[inline] + fn read_i8_array(reader: &mut NbtReader) -> NbtResult> { + let len = reader.read_le_i32() as usize; + let value = reader.read_i8_array(len); + Ok(value) + } + #[inline] + fn read_i32_array(reader: &mut NbtReader) -> NbtResult> { + let len = reader.read_le_i32() as usize; + let value = reader.read_i32_array(len); + Ok(value) + } + #[inline] + fn read_i64_array(reader: &mut NbtReader) -> NbtResult> { + let len = reader.read_le_i32() as usize; + let value = reader.read_i64_array(len); + Ok(value) + } + #[inline] + fn read_compound(reader: &mut NbtReader) -> NbtResult> { + let mut compound = Vec::with_capacity(10); + loop { + let tag_id = reader.read_u8(); + if tag_id == 0 { + break; + } + let name = BedrockDisk::read_nbt_string(reader)?; + let value = match tag_id { + 1 => NbtValue::Byte(reader.read_i8()), + 2 => NbtValue::Short(reader.read_le_i16()), + 3 => NbtValue::Int(reader.read_le_i32()), + 4 => NbtValue::Long(reader.read_le_i64()), + 5 => NbtValue::Float(reader.read_le_f32()), + 6 => NbtValue::Double(reader.read_le_f64()), + 7 => NbtValue::ByteArray(BedrockDisk::read_i8_array(reader)?), + 8 => NbtValue::String(BedrockDisk::read_nbt_string(reader)?), + 9 => NbtValue::List(BedrockDisk::read_list(reader)?), + 10 => NbtValue::Compound(None, nbt_version::BedrockDisk::read_compound(reader)?), + 11 => NbtValue::IntArray(BedrockDisk::read_i32_array(reader)?), + 12 => NbtValue::LongArray(BedrockDisk::read_i64_array(reader)?), + _ => unimplemented!(), + }; + compound.push((name, value)); + } + Ok(compound) + } + #[inline] + fn read_list(reader: &mut NbtReader) -> NbtResult> { + let type_id = reader.read_u8(); + let len = reader.read_le_i32() as usize; + let mut list = Vec::with_capacity(len); + for _ in 0..len { + let value = match type_id { + 1 => NbtValue::Byte(reader.read_i8()), + 2 => NbtValue::Short(reader.read_le_i16()), + 3 => NbtValue::Int(reader.read_le_i32()), + 4 => NbtValue::Long(reader.read_le_i64()), + 5 => NbtValue::Float(reader.read_le_f32()), + 6 => NbtValue::Double(reader.read_le_f64()), + 7 => NbtValue::ByteArray(BedrockDisk::read_i8_array(reader)?), + 8 => NbtValue::String(BedrockDisk::read_nbt_string(reader)?), + 9 => NbtValue::List(BedrockDisk::read_list(reader)?), + 10 => NbtValue::Compound(None, nbt_version::BedrockDisk::read_compound(reader)?), + 11 => NbtValue::IntArray(BedrockDisk::read_i32_array(reader)?), + 12 => NbtValue::LongArray(BedrockDisk::read_i64_array(reader)?), + _ => unimplemented!(), + }; + list.push(value); + } + Ok(list) + } + + fn from_reader(mut reader: NbtReader) -> NbtResult { + // 第一个 tag, 不可能是 0 + match reader.read_u8() { + 9 => { + // 基岩版的 NBT 根节点可以是一个 List + Ok(NbtValue::List(nbt_version::BedrockDisk::read_list(&mut reader)?)) + } + 10 => { + // 或者一个有名字的 Compound + let name = BedrockDisk::read_nbt_string(&mut reader)?; + Ok(NbtValue::Compound( + Some(name), + nbt_version::BedrockDisk::read_compound(&mut reader)?, + )) + } + // 别的不行 + x => Err(NbtError::WrongRootType(x)), + } + } +} + +/// 最痛苦的来了 +impl NbtReadTrait for BedrockNetVarInt { + fn read_nbt_string(reader: &mut NbtReader) -> NbtResult { + let len = reader.read_var_i32()? as usize; + reader.read_string(len) + } + fn read_i8_array(reader: &mut NbtReader) -> NbtResult> { + let len = reader.read_var_i32()? as usize; + let value = reader.read_i8_array(len); + Ok(value) + } + fn read_i32_array(reader: &mut NbtReader) -> NbtResult> { + let len = reader.read_var_i32()? as usize; + let value = reader.read_i32_array(len); + Ok(value) + } + fn read_i64_array(reader: &mut NbtReader) -> NbtResult> { + let len = reader.read_var_i32()? as usize; + let value = reader.read_i64_array(len); + Ok(value) + } + fn read_compound(reader: &mut NbtReader) -> NbtResult> { todo!() } + fn read_list(reader: &mut NbtReader) -> NbtResult> { todo!() } + fn from_reader(mut reader: NbtReader) -> NbtResult { + match reader.read_u8() { + 9 => { + // 基岩版的 NBT 根节点可以是一个 List + Ok(NbtValue::List(BedrockNetVarInt::read_list(&mut reader)?)) + } + 10 => { + // 或者一个有名字的 Compound + let name = BedrockNetVarInt::read_nbt_string(&mut reader)?; + Ok(NbtValue::Compound(Some(name), BedrockNetVarInt::read_compound(&mut reader)?)) + } + // 别的不行 + x => Err(NbtError::WrongRootType(x)), + } + } +} macro_rules! read_uncheck { ($be_name:ident, $le_name:ident, $ty:ty, $size:literal) => { @@ -266,9 +397,51 @@ impl NbtReader<'_> { self.cursor += 4; value } - /// 安全的读取 i32 类型的数据 + /// 安全的读取一个 Varint 数据 /// - /// 转换大小端(小端) + /// 他有大小端区别吗? (其实是小端) + /// + /// 会在超出长度时 panic + #[inline] + pub fn read_var_i32(&mut self) -> NbtResult { + let mut value = 0; + let mut size = 0; + loop { + let byte = self.read_u8(); + value |= ((byte & 0b0111_1111) as i32) << (size * 7); + size += 1; + if size > 5 { + return Err(NbtError::VarIntTooBig(value as usize)); + } + if (byte & 0b1000_0000) == 0 { + break; + } + } + Ok(value) + } + /// 安全的读取一个 Varlong + /// + /// 他有大小端区别吗? (其实是小端) + /// + /// 会在超出长度时 panic + #[inline] + pub fn read_var_i64(&mut self) -> NbtResult { + let mut value = 0; + let mut size = 0; + loop { + let byte = self.read_u8(); + value |= ((byte & 0b0111_1111) as i64) << (size * 7); + size += 1; + if size > 10 { + return Err(NbtError::VarlongTooBig(value as usize)); + } + if (byte & 0b1000_0000) == 0 { + break; + } + } + Ok(value) + } + /// 安全的读取一个小端 i32 数据 /// /// 会在超出长度时 panic #[inline]