更新了NBT读取和写入的功能

This commit is contained in:
shenjack 2024-03-10 14:07:29 +08:00
parent 5e5bf7d863
commit 393083891c
Signed by: shenjack
GPG Key ID: 7B1134A979775551
5 changed files with 319 additions and 21 deletions

View File

@ -4,16 +4,21 @@
目前进度 目前进度
- [ ] 读取 - [x] 读取
- [x] Java - [x] Java
- [x] JavaNetAfter1_20_2 - [x] JavaNetAfter1_20_2
- [x] BedrockDisk - [x] BedrockDisk
- [x] BedrockNetVarInt - [x] BedrockNetVarInt
- [ ] 写入 - [ ] 写入
- [ ] Java - [x] Java
- [ ] JavaNetAfter1_20_2 - [x] JavaNetAfter1_20_2
- [ ] BedrockDisk - [ ] BedrockDisk
- [ ] BedrockNetVarInt - [ ] BedrockNetVarInt
- [ ] `Serde` 支持
- [ ] `Serialize`
- [ ] `Deserialize`
- [ ] `from_value`
- [ ] `to_value`
支持 支持

View File

@ -1,4 +1,5 @@
pub mod reader; pub mod reader;
pub mod writer;
use reader::NbtReader; use reader::NbtReader;
@ -32,10 +33,21 @@ pub mod nbt_version {
fn write_i32_array(writer: &mut Vec<u8>, data: &[i32]); fn write_i32_array(writer: &mut Vec<u8>, data: &[i32]);
fn write_i64_array(writer: &mut Vec<u8>, data: &[i64]); fn write_i64_array(writer: &mut Vec<u8>, data: &[i64]);
fn write_nbt_string(writer: &mut Vec<u8>, data: &str); fn write_nbt_string(writer: &mut Vec<u8>, data: &str);
fn write_list(writer: &mut Vec<u8>, data: &[NbtValue]); fn write_list(writer: &mut Vec<u8>, data: &[NbtValue]) -> NbtResult<()>;
fn write_compound(writer: &mut Vec<u8>, data: &[(String, NbtValue)]); fn write_compound(
writer: &mut Vec<u8>,
name: Option<&String>,
data: &[(String, NbtValue)],
) -> NbtResult<()>;
fn to_writer(value: &NbtValue, writer: &mut Vec<u8>) -> NbtResult<()>; fn write_to(value: &NbtValue, buff: &mut Vec<u8>) -> NbtResult<()>;
fn write_to_with_name(name: &str, value: &NbtValue, buff: &mut Vec<u8>) -> NbtResult<()>;
fn to_binary(value: &NbtValue) -> NbtResult<Vec<u8>> {
let mut buff = Vec::new();
Self::write_to(value, &mut buff)?;
Ok(buff)
}
} }
pub trait NbtReadTrait { pub trait NbtReadTrait {
@ -121,6 +133,8 @@ pub enum NbtError {
VarIntTooBig(usize), VarIntTooBig(usize),
/// Varlong 过大 /// Varlong 过大
VarlongTooBig(usize), VarlongTooBig(usize),
/// NbtList 中类型不同
ListTypeNotSame(Vec<NbtTypeId>),
} }
pub type NbtResult<T> = Result<T, NbtError>; pub type NbtResult<T> = Result<T, NbtError>;
@ -129,16 +143,17 @@ impl std::fmt::Display for NbtError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self { match self {
NbtError::UnknownErr(s) => write!(f, "未知错误: {}", s), NbtError::UnknownErr(s) => write!(f, "未知错误: {}", s),
NbtError::WrongRootType(n) => { NbtError::WrongRootType(n) => match n {
match n { 9 => {
9 => { write!(
write!(f, "根节点为 NbtList(9) 类型, 是否应该使用 BedrockDisk/BedrockNetVarInt 解析?") f,
} "根节点为 NbtList(9) 类型, 是否应该使用 BedrockDisk/BedrockNetVarInt?"
_ => { )
write!(f, "根节点类型错误: {}, 应为 NbtCompound/NbtList(bedrock only)", n)
}
} }
} _ => {
write!(f, "根节点类型错误: {}, 应为 NbtCompound/NbtList(bedrock only)", n)
}
},
NbtError::RootWithoutName => { NbtError::RootWithoutName => {
write!(f, "根节点无名称, 是否应该使用 JavaNetAfter1_20_2 解析?") write!(f, "根节点无名称, 是否应该使用 JavaNetAfter1_20_2 解析?")
} }
@ -160,6 +175,9 @@ impl std::fmt::Display for NbtError {
), ),
NbtError::VarIntTooBig(n) => write!(f, "VarInt 过大: {} 最大长度为 5", n), NbtError::VarIntTooBig(n) => write!(f, "VarInt 过大: {} 最大长度为 5", n),
NbtError::VarlongTooBig(n) => write!(f, "VarLong 过大: {} 最大长度为 10", 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 { impl NbtValue {
pub fn from_binary<T>(data: &mut [u8]) -> NbtResult<NbtValue> pub fn from_binary<R>(data: &mut [u8]) -> NbtResult<NbtValue>
where where
T: nbt_version::NbtReadTrait, R: nbt_version::NbtReadTrait,
{ {
let reader = NbtReader::new(data); 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<W>(&self, buff: &mut Vec<u8>) -> NbtResult<()>
where
W: nbt_version::NbtWriteTrait,
{
W::write_to(self, buff)
}
pub fn write_to_with_name<W>(&self, name: &str, buff: &mut Vec<u8>) -> NbtResult<()>
where
W: nbt_version::NbtWriteTrait,
{
W::write_to_with_name(name, self, buff)
}
pub fn to_binary<W>(&self) -> NbtResult<Vec<u8>>
where
W: nbt_version::NbtWriteTrait,
{
W::to_binary(self)
} }
} }

View File

@ -7,8 +7,6 @@ pub struct NbtReader<'data> {
pub data: &'data mut [u8], pub data: &'data mut [u8],
/// 当前读取的位置 /// 当前读取的位置
pub cursor: usize, pub cursor: usize,
// be/le
// pub endian: Endian,
} }
/// Java 版 绝大部分的 NBT 格式 /// Java 版 绝大部分的 NBT 格式

View File

@ -298,7 +298,7 @@ mod nbt {
Some("hello world".to_string()), Some("hello world".to_string()),
vec![("name".to_string(), NbtValue::String("Bananrama".to_string()))], vec![("name".to_string(), NbtValue::String("Bananrama".to_string()))],
); );
assert_eq!(data, Ok(correct_data)) assert_eq!(data, Ok(correct_data));
} }
#[test] #[test]
@ -316,6 +316,41 @@ mod nbt {
assert_eq!(data, Ok(correct_data)) 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<u8> = Vec::new();
data.write_to::<nbt_version::Java>(&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<u8> = Vec::new();
data.write_to::<nbt_version::JavaNetAfter1_20_2>(&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] #[test]
fn big_test() { fn big_test() {
let mut data: [u8; 0x608] = [ let mut data: [u8; 0x608] = [

204
shen-nbt5/src/writer.rs Normal file
View File

@ -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<u8>, 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::<Vec<u8>>().as_slice());
}
#[inline]
fn write_i32_array(writer: &mut Vec<u8>, 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::<Vec<[u8; 4]>>().concat(),
);
}
#[inline]
fn write_i64_array(writer: &mut Vec<u8>, 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::<Vec<[u8; 8]>>().concat(),
);
}
#[inline]
fn write_nbt_string(writer: &mut Vec<u8>, 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<u8>, 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<u8>,
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<u8>) -> 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<u8>) -> 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<u8>, data: &[i8]) { Java::write_i8_array(writer, data); }
#[inline]
fn write_i32_array(writer: &mut Vec<u8>, data: &[i32]) { Java::write_i32_array(writer, data); }
#[inline]
fn write_i64_array(writer: &mut Vec<u8>, data: &[i64]) { Java::write_i64_array(writer, data); }
#[inline]
fn write_nbt_string(writer: &mut Vec<u8>, data: &str) { Java::write_nbt_string(writer, data); }
#[inline]
fn write_list(writer: &mut Vec<u8>, data: &[NbtValue]) -> NbtResult<()> {
Java::write_list(writer, data)
}
#[inline]
fn write_compound(
writer: &mut Vec<u8>,
name: Option<&String>,
data: &[(String, NbtValue)],
) -> NbtResult<()> {
Java::write_compound(writer, name, data)
}
#[inline]
fn write_to(value: &NbtValue, buff: &mut Vec<u8>) -> 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<u8>) -> NbtResult<()> {
// drop name
JavaNetAfter1_20_2::write_to(value, buff)
}
#[inline]
fn to_binary(value: &NbtValue) -> NbtResult<Vec<u8>> {
let mut buff = Vec::new();
JavaNetAfter1_20_2::write_to(value, &mut buff)?;
Ok(buff)
}
}