更新了NBT读取和写入的功能
This commit is contained in:
parent
5e5bf7d863
commit
393083891c
11
README.md
11
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`
|
||||
|
||||
支持
|
||||
|
||||
|
@ -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<u8>, data: &[i32]);
|
||||
fn write_i64_array(writer: &mut Vec<u8>, data: &[i64]);
|
||||
fn write_nbt_string(writer: &mut Vec<u8>, data: &str);
|
||||
fn write_list(writer: &mut Vec<u8>, data: &[NbtValue]);
|
||||
fn write_compound(writer: &mut Vec<u8>, data: &[(String, NbtValue)]);
|
||||
fn write_list(writer: &mut Vec<u8>, data: &[NbtValue]) -> NbtResult<()>;
|
||||
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 {
|
||||
@ -121,6 +133,8 @@ pub enum NbtError {
|
||||
VarIntTooBig(usize),
|
||||
/// Varlong 过大
|
||||
VarlongTooBig(usize),
|
||||
/// NbtList 中类型不同
|
||||
ListTypeNotSame(Vec<NbtTypeId>),
|
||||
}
|
||||
|
||||
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 {
|
||||
match self {
|
||||
NbtError::UnknownErr(s) => write!(f, "未知错误: {}", s),
|
||||
NbtError::WrongRootType(n) => {
|
||||
match n {
|
||||
NbtError::WrongRootType(n) => match n {
|
||||
9 => {
|
||||
write!(f, "根节点为 NbtList(9) 类型, 是否应该使用 BedrockDisk/BedrockNetVarInt 解析?")
|
||||
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<T>(data: &mut [u8]) -> NbtResult<NbtValue>
|
||||
pub fn from_binary<R>(data: &mut [u8]) -> NbtResult<NbtValue>
|
||||
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<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)
|
||||
}
|
||||
}
|
||||
|
@ -7,8 +7,6 @@ pub struct NbtReader<'data> {
|
||||
pub data: &'data mut [u8],
|
||||
/// 当前读取的位置
|
||||
pub cursor: usize,
|
||||
// be/le
|
||||
// pub endian: Endian,
|
||||
}
|
||||
|
||||
/// Java 版 绝大部分的 NBT 格式
|
||||
|
@ -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<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]
|
||||
fn big_test() {
|
||||
let mut data: [u8; 0x608] = [
|
||||
|
204
shen-nbt5/src/writer.rs
Normal file
204
shen-nbt5/src/writer.rs
Normal 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)
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user