Skip to content

Commit

Permalink
work on deserialization
Browse files Browse the repository at this point in the history
  • Loading branch information
kralverde committed Feb 14, 2025
1 parent aa069b7 commit 2fe17e0
Show file tree
Hide file tree
Showing 15 changed files with 382 additions and 209 deletions.
27 changes: 12 additions & 15 deletions pumpkin-nbt/src/compound.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::deserializer::ReadAdaptor;
use crate::serializer::WriteAdaptor;
use crate::tag::NbtTag;
use crate::{get_nbt_string, Error, Nbt, END_ID};
use bytes::{BufMut, Bytes, BytesMut};
use std::io::{ErrorKind, Read, Write};
use std::vec::IntoIter;

Expand Down Expand Up @@ -56,19 +56,16 @@ impl NbtCompound {
Ok(compound)
}

pub fn serialize_content(&self) -> Bytes {
let mut bytes = BytesMut::new();
pub fn serialize_content<W>(&self, w: &mut WriteAdaptor<W>) -> Result<(), Error>
where
W: Write,
{
for (name, tag) in &self.child_tags {
bytes.put_u8(tag.get_type_id());
bytes.put(NbtTag::String(name.clone()).serialize_data());
bytes.put(tag.serialize_data());
w.write_u8_be(tag.get_type_id())?;
NbtTag::String(name.clone()).serialize_data(w)?;
tag.serialize_data(w)?;
}
bytes.put_u8(END_ID);
bytes.freeze()
}

pub fn serialize_content_to_writer<W: Write>(&self, mut writer: W) -> std::io::Result<()> {
writer.write_all(&self.serialize_content())?;
w.write_u8_be(END_ID)?;
Ok(())
}

Expand Down Expand Up @@ -152,19 +149,19 @@ impl NbtCompound {
self.get(name).and_then(|tag| tag.extract_string())
}

pub fn get_list(&self, name: &str) -> Option<&Vec<NbtTag>> {
pub fn get_list(&self, name: &str) -> Option<&[NbtTag]> {
self.get(name).and_then(|tag| tag.extract_list())
}

pub fn get_compound(&self, name: &str) -> Option<&NbtCompound> {
self.get(name).and_then(|tag| tag.extract_compound())
}

pub fn get_int_array(&self, name: &str) -> Option<&Vec<i32>> {
pub fn get_int_array(&self, name: &str) -> Option<&[i32]> {
self.get(name).and_then(|tag| tag.extract_int_array())
}

pub fn get_long_array(&self, name: &str) -> Option<&Vec<i64>> {
pub fn get_long_array(&self, name: &str) -> Option<&[i64]> {
self.get(name).and_then(|tag| tag.extract_long_array())
}
}
Expand Down
57 changes: 54 additions & 3 deletions pumpkin-nbt/src/deserializer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ impl<R: Read> ReadAdaptor<R> {
Ok(f64::from_be_bytes(buf))
}

pub fn read_to_bytes(&mut self, count: usize) -> Result<Bytes> {
pub fn read_boxed_slice(&mut self, count: usize) -> Result<Box<[u8]>> {
let mut buf = vec![0u8; count];
self.reader
.read_exact(&mut buf)
Expand All @@ -113,6 +113,7 @@ impl<R: Read> ReadAdaptor<R> {
pub struct Deserializer<R: Read> {
input: ReadAdaptor<R>,
tag_to_deserialize: Option<u8>,
in_list: bool,
is_named: bool,
}

Expand All @@ -121,6 +122,7 @@ impl<R: Read> Deserializer<R> {
Deserializer {
input: ReadAdaptor { reader: input },
tag_to_deserialize: None,
in_list: false,
is_named,
}
}
Expand Down Expand Up @@ -148,7 +150,7 @@ impl<'de, R: Read> de::Deserializer<'de> for &mut Deserializer<R> {
type Error = Error;

forward_to_deserialize_any! {
u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string unit unit_struct seq tuple tuple_struct
i8 i16 i32 i64 f32 f64 char str string unit unit_struct seq tuple tuple_struct
ignored_any bytes enum newtype_struct byte_buf option
}

Expand Down Expand Up @@ -195,6 +197,51 @@ impl<'de, R: Read> de::Deserializer<'de> for &mut Deserializer<R> {
result
}

fn deserialize_u8<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
if self.in_list {
let value = self.input.get_u8_be()?;
visitor.visit_u8::<Error>(value)
} else {
panic!("{:?}", self.tag_to_deserialize);

/*
Err(Error::UnsupportedType(
"u8; NBT only supports signed values".to_string(),
))
*/
}
}

fn deserialize_u16<V>(self, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
Err(Error::UnsupportedType(
"u16; NBT only supports signed values".to_string(),
))
}

fn deserialize_u32<V>(self, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
Err(Error::UnsupportedType(
"u32; NBT only supports signed values".to_string(),
))
}

fn deserialize_u64<V>(self, _visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
{
Err(Error::UnsupportedType(
"u64; NBT only supports signed values".to_string(),
))
}

fn deserialize_bool<V>(self, visitor: V) -> Result<V::Value>
where
V: Visitor<'de>,
Expand Down Expand Up @@ -304,6 +351,10 @@ impl<'de, R: Read> SeqAccess<'de> for ListAccess<'_, R> {

self.remaining_values -= 1;
self.de.tag_to_deserialize = Some(self.list_type);
seed.deserialize(&mut *self.de).map(Some)
self.de.in_list = true;
let result = seed.deserialize(&mut *self.de).map(Some);
self.de.in_list = false;
result
}
}

77 changes: 60 additions & 17 deletions pumpkin-nbt/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ use std::{
ops::Deref,
};

use bytes::{BufMut, Bytes, BytesMut};
use bytes::Bytes;
use compound::NbtCompound;
use deserializer::ReadAdaptor;
use serde::{de, ser};
use serde::{Deserialize, Deserializer};
use serializer::WriteAdaptor;
use tag::NbtTag;
use thiserror::Error;

Expand Down Expand Up @@ -49,6 +50,8 @@ pub enum Error {
Incomplete(io::Error),
#[error("Negative list length {0}")]
NegativeLength(i32),
#[error("Length too large {0}")]
LargeLength(usize),
}

impl ser::Error for Error {
Expand Down Expand Up @@ -111,11 +114,15 @@ impl Nbt {
}

pub fn write(&self) -> Bytes {
let mut bytes = BytesMut::new();
bytes.put_u8(COMPOUND_ID);
bytes.put(NbtTag::String(self.name.to_string()).serialize_data());
bytes.put(self.root_tag.serialize_content());
bytes.freeze()
let mut bytes = Vec::new();
let mut writer = WriteAdaptor::new(&mut bytes);
writer.write_u8_be(COMPOUND_ID).unwrap();
NbtTag::String(self.name.to_string())
.serialize_data(&mut writer)
.unwrap();
self.root_tag.serialize_content(&mut writer).unwrap();

bytes.into()
}

pub fn write_to_writer<W: Write>(&self, mut writer: W) -> Result<(), io::Error> {
Expand All @@ -125,10 +132,13 @@ impl Nbt {

/// Writes NBT tag, without name of root compound.
pub fn write_unnamed(&self) -> Bytes {
let mut bytes = BytesMut::new();
bytes.put_u8(COMPOUND_ID);
bytes.put(self.root_tag.serialize_content());
bytes.freeze()
let mut bytes = Vec::new();
let mut writer = WriteAdaptor::new(&mut bytes);

writer.write_u8_be(COMPOUND_ID).unwrap();
self.root_tag.serialize_content(&mut writer).unwrap();

bytes.into()
}

pub fn write_unnamed_to_writer<W: Write>(&self, mut writer: W) -> Result<(), io::Error> {
Expand Down Expand Up @@ -169,7 +179,7 @@ impl AsMut<NbtCompound> for Nbt {

pub fn get_nbt_string<R: Read>(bytes: &mut ReadAdaptor<R>) -> Result<String, Error> {
let len = bytes.get_u16_be()? as usize;
let string_bytes = bytes.read_to_bytes(len)?;
let string_bytes = bytes.read_boxed_slice(len)?;
let string = cesu8::from_java_cesu8(&string_bytes).map_err(|_| Error::Cesu8DecodingError)?;
Ok(string.to_string())
}
Expand Down Expand Up @@ -204,6 +214,7 @@ impl_array!(BytesArray, "byte");

#[cfg(test)]
mod test {
use std::io::Read;
use std::sync::LazyLock;

use flate2::read::GzDecoder;
Expand All @@ -212,6 +223,7 @@ mod test {
use pumpkin_world::world_info::{DataPacks, LevelData, WorldGenSettings, WorldVersion};

use crate::deserializer::from_bytes;
use crate::serializer::to_bytes;
use crate::BytesArray;
use crate::IntArray;
use crate::LongArray;
Expand All @@ -237,8 +249,10 @@ mod test {
float: 1.00,
string: "Hello test".to_string(),
};
let bytes = to_bytes_unnamed(&test).unwrap();
let recreated_struct: Test = from_bytes_unnamed(&mut &bytes[..]).unwrap();

let mut bytes = Vec::new();
to_bytes_unnamed(&test, &mut bytes).unwrap();
let recreated_struct: Test = from_bytes_unnamed(&bytes[..]).unwrap();

assert_eq!(test, recreated_struct);
}
Expand All @@ -260,8 +274,10 @@ mod test {
int_array: vec![13, 1321, 2],
long_array: vec![1, 0, 200301, 1],
};
let bytes = to_bytes_unnamed(&test).unwrap();
let recreated_struct: TestArray = from_bytes_unnamed(&mut &bytes[..]).unwrap();

let mut bytes = Vec::new();
to_bytes_unnamed(&test, &mut bytes).unwrap();
let recreated_struct: TestArray = from_bytes_unnamed(&bytes[..]).unwrap();

assert_eq!(test, recreated_struct);
}
Expand Down Expand Up @@ -316,13 +332,40 @@ mod test {
});

#[test]
fn test_serialize_deserialize_level_dat() {
fn test_deserialize_level_dat() {
let raw_compressed_nbt = include_bytes!("../assets/level.dat");
assert!(!raw_compressed_nbt.is_empty());

let decoder = GzDecoder::new(&raw_compressed_nbt[..]);
let level_dat: LevelDat = from_bytes(decoder).unwrap();
let level_dat: LevelDat = from_bytes(decoder).expect("Failed to decode from file");

assert_eq!(level_dat, *LEVEL_DAT);
}

#[test]
fn test_serialize_level_dat() {
let raw_compressed_nbt = include_bytes!("../assets/level.dat");
assert!(!raw_compressed_nbt.is_empty());

let mut decoder = GzDecoder::new(&raw_compressed_nbt[..]);
let mut raw_bytes = Vec::new();
decoder.read_to_end(&mut raw_bytes).unwrap();

let mut serialized = Vec::new();
to_bytes(&*LEVEL_DAT, "".to_string(), &mut serialized).expect("Failed to encode to bytes");
raw_bytes
.iter()
.zip(serialized.iter())
.enumerate()
.for_each(|(index, (expected_byte, serialized_byte))| {
if expected_byte != serialized_byte {
panic!("{} vs {} ({})", expected_byte, serialized_byte, index);
}
});

let level_dat_again: LevelDat =
from_bytes(&serialized[..]).expect("Failed to decode from bytes");

assert_eq!(level_dat_again, *LEVEL_DAT);
}
}
Loading

0 comments on commit 2fe17e0

Please sign in to comment.