mirror of
https://github.com/mat-1/azalea.git
synced 2024-09-18 22:32:31 +00:00
reading nbt
This commit is contained in:
parent
428d0ee0e6
commit
76e1985fc4
12 changed files with 324 additions and 5 deletions
38
Cargo.lock
generated
38
Cargo.lock
generated
|
@ -80,6 +80,15 @@ dependencies = [
|
|||
"uuid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "azalea-nbt"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"byteorder",
|
||||
"num-derive",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "azalea-protocol"
|
||||
version = "0.1.0"
|
||||
|
@ -417,6 +426,26 @@ dependencies = [
|
|||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-derive"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "876a53fff98e03a936a674b29568b0e605f06b29372c2489ff4de23f1949743d"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9a64b1ec5cda2586e284722486d802acf1f7dbdc623e2bfc57e65ca1cd099290"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.13.0"
|
||||
|
@ -673,11 +702,10 @@ checksum = "cda74da7e1a664f795bb1f8a87ec406fb89a02522cf6e50620d016add6dbbf5c"
|
|||
|
||||
[[package]]
|
||||
name = "tokio"
|
||||
version = "1.14.0"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "70e992e41e0d2fb9f755b37446f20900f64446ef54874f40a60c78f021ac6144"
|
||||
checksum = "fbbf1c778ec206785635ce8ad57fe52b3009ae9e0c9f574a728f3049d3e55838"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bytes",
|
||||
"libc",
|
||||
"memchr",
|
||||
|
@ -690,9 +718,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "tokio-macros"
|
||||
version = "1.6.0"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c9efc1aba077437943f7515666aa2b882dfabfbfdf89c819ea75a8d6e9eaba5e"
|
||||
checksum = "b557f72f448c511a979e2564e55d74e6c4432fc96ff4f6241bc6bded342643b7"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
|
|
|
@ -7,4 +7,5 @@ members = [
|
|||
"azalea-chat",
|
||||
"azalea-core",
|
||||
"azalea-auth",
|
||||
"azalea-nbt",
|
||||
]
|
||||
|
|
11
azalea-nbt/Cargo.toml
Normal file
11
azalea-nbt/Cargo.toml
Normal file
|
@ -0,0 +1,11 @@
|
|||
[package]
|
||||
edition = "2021"
|
||||
name = "azalea-nbt"
|
||||
version = "0.1.0"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
byteorder = "^1.4.3"
|
||||
num-derive = "^0.3.3"
|
||||
num-traits = "^0.2.14"
|
3
azalea-nbt/README.md
Normal file
3
azalea-nbt/README.md
Normal file
|
@ -0,0 +1,3 @@
|
|||
Deserialize Minecraft NBT. This is somewhat based on [Hermatite NBT](https://github.com/PistonDevelopers/hematite_nbt).
|
||||
|
||||
|
111
azalea-nbt/src/decode.rs
Normal file
111
azalea-nbt/src/decode.rs
Normal file
|
@ -0,0 +1,111 @@
|
|||
use crate::Error;
|
||||
use crate::Tag;
|
||||
use byteorder::{ReadBytesExt, BE};
|
||||
use std::{collections::HashMap, io::Read};
|
||||
|
||||
impl Tag {
|
||||
fn read_known(stream: &mut impl Read, id: u8) -> Result<Tag, Error> {
|
||||
let tag = match id {
|
||||
// Signifies the end of a TAG_Compound. It is only ever used inside
|
||||
// a TAG_Compound, and is not named despite being in a TAG_Compound
|
||||
0 => Tag::End,
|
||||
// A single signed byte
|
||||
1 => Tag::Byte(stream.read_i8().map_err(|_| Error::InvalidTag)?),
|
||||
// A single signed, big endian 16 bit integer
|
||||
2 => Tag::Short(stream.read_i16::<BE>().map_err(|_| Error::InvalidTag)?),
|
||||
// A single signed, big endian 32 bit integer
|
||||
3 => Tag::Int(stream.read_i32::<BE>().map_err(|_| Error::InvalidTag)?),
|
||||
// A single signed, big endian 64 bit integer
|
||||
4 => Tag::Long(stream.read_i64::<BE>().map_err(|_| Error::InvalidTag)?),
|
||||
// A single, big endian IEEE-754 single-precision floating point
|
||||
// number (NaN possible)
|
||||
5 => Tag::Float(stream.read_f32::<BE>().map_err(|_| Error::InvalidTag)?),
|
||||
// A single, big endian IEEE-754 double-precision floating point
|
||||
// number (NaN possible)
|
||||
6 => Tag::Double(stream.read_f64::<BE>().map_err(|_| Error::InvalidTag)?),
|
||||
// A length-prefixed array of signed bytes. The prefix is a signed
|
||||
// integer (thus 4 bytes)
|
||||
7 => {
|
||||
let length = stream.read_i32::<BE>().map_err(|_| Error::InvalidTag)?;
|
||||
let mut bytes = Vec::new();
|
||||
for _ in 0..length {
|
||||
bytes.push(stream.read_i8().map_err(|_| Error::InvalidTag)?);
|
||||
}
|
||||
Tag::ByteArray(bytes)
|
||||
}
|
||||
// A length-prefixed modified UTF-8 string. The prefix is an
|
||||
// unsigned short (thus 2 bytes) signifying the length of the
|
||||
// string in bytes
|
||||
8 => {
|
||||
let length = stream.read_u16::<BE>().map_err(|_| Error::InvalidTag)?;
|
||||
let mut bytes = Vec::new();
|
||||
for _ in 0..length {
|
||||
bytes.push(stream.read_u8().map_err(|_| Error::InvalidTag)?);
|
||||
}
|
||||
Tag::String(String::from_utf8(bytes).map_err(|_| Error::InvalidTag)?)
|
||||
}
|
||||
// A list of nameless tags, all of the same type. The list is
|
||||
// prefixed with the Type ID of the items it contains (thus 1
|
||||
// byte), and the length of the list as a signed integer (a further
|
||||
// 4 bytes). If the length of the list is 0 or negative, the type
|
||||
// may be 0 (TAG_End) but otherwise it must be any other type. (The
|
||||
// notchian implementation uses TAG_End in that situation, but
|
||||
// another reference implementation by Mojang uses 1 instead;
|
||||
// parsers should accept any type if the length is <= 0).
|
||||
9 => {
|
||||
let type_id = stream.read_u8().map_err(|_| Error::InvalidTag)?;
|
||||
let length = stream.read_i32::<BE>().map_err(|_| Error::InvalidTag)?;
|
||||
let mut list = Vec::new();
|
||||
for _ in 0..length {
|
||||
list.push(Tag::read_known(stream, type_id)?);
|
||||
}
|
||||
Tag::List(list)
|
||||
}
|
||||
// Effectively a list of a named tags. Order is not guaranteed.
|
||||
10 => {
|
||||
let mut map = HashMap::new();
|
||||
loop {
|
||||
let tag_id = stream.read_u8().unwrap_or(0);
|
||||
if tag_id == 0 {
|
||||
break;
|
||||
}
|
||||
let name = match Tag::read_known(stream, 8)? {
|
||||
Tag::String(name) => name,
|
||||
_ => panic!("Expected a string tag"),
|
||||
};
|
||||
let tag = Tag::read_known(stream, tag_id).map_err(|_| Error::InvalidTag)?;
|
||||
map.insert(name, tag);
|
||||
}
|
||||
Tag::Compound(map)
|
||||
}
|
||||
// A length-prefixed array of signed integers. The prefix is a
|
||||
// signed integer (thus 4 bytes) and indicates the number of 4 byte
|
||||
// integers.
|
||||
11 => {
|
||||
let length = stream.read_i32::<BE>().map_err(|_| Error::InvalidTag)?;
|
||||
let mut ints = Vec::new();
|
||||
for _ in 0..length {
|
||||
ints.push(stream.read_i32::<BE>().map_err(|_| Error::InvalidTag)?);
|
||||
}
|
||||
Tag::IntArray(ints)
|
||||
}
|
||||
// A length-prefixed array of signed longs. The prefix is a signed
|
||||
// integer (thus 4 bytes) and indicates the number of 8 byte longs.
|
||||
12 => {
|
||||
let length = stream.read_i32::<BE>().map_err(|_| Error::InvalidTag)?;
|
||||
let mut longs = Vec::new();
|
||||
for _ in 0..length {
|
||||
longs.push(stream.read_i64::<BE>().map_err(|_| Error::InvalidTag)?);
|
||||
}
|
||||
Tag::LongArray(longs)
|
||||
}
|
||||
_ => return Err(Error::InvalidTagType(id)),
|
||||
};
|
||||
Ok(tag)
|
||||
}
|
||||
|
||||
pub fn read(stream: &mut impl Read) -> Result<Tag, Error> {
|
||||
// default to compound tag
|
||||
Tag::read_known(stream, 10)
|
||||
}
|
||||
}
|
117
azalea-nbt/src/encode.rs
Normal file
117
azalea-nbt/src/encode.rs
Normal file
|
@ -0,0 +1,117 @@
|
|||
use byteorder::{ReadBytesExt, BE};
|
||||
use error::Error;
|
||||
use std::{collections::HashMap, io::Read};
|
||||
use tag::Tag;
|
||||
|
||||
impl Tag {
|
||||
fn write(&self, stream: &mut impl Read) -> Result<(), Error> {
|
||||
println!("read_known: id={}", id);
|
||||
let tag = match id {
|
||||
// Signifies the end of a TAG_Compound. It is only ever used inside
|
||||
// a TAG_Compound, and is not named despite being in a TAG_Compound
|
||||
0 => Tag::End,
|
||||
// A single signed byte
|
||||
1 => Tag::Byte(stream.read_i8().map_err(|_| Error::InvalidTag)?),
|
||||
// A single signed, big endian 16 bit integer
|
||||
2 => Tag::Short(stream.read_i16::<BE>().map_err(|_| Error::InvalidTag)?),
|
||||
// A single signed, big endian 32 bit integer
|
||||
3 => Tag::Int(stream.read_i32::<BE>().map_err(|_| Error::InvalidTag)?),
|
||||
// A single signed, big endian 64 bit integer
|
||||
4 => Tag::Long(stream.read_i64::<BE>().map_err(|_| Error::InvalidTag)?),
|
||||
// A single, big endian IEEE-754 single-precision floating point
|
||||
// number (NaN possible)
|
||||
5 => Tag::Float(stream.read_f32::<BE>().map_err(|_| Error::InvalidTag)?),
|
||||
// A single, big endian IEEE-754 double-precision floating point
|
||||
// number (NaN possible)
|
||||
6 => Tag::Double(stream.read_f64::<BE>().map_err(|_| Error::InvalidTag)?),
|
||||
// A length-prefixed array of signed bytes. The prefix is a signed
|
||||
// integer (thus 4 bytes)
|
||||
7 => {
|
||||
let length = stream.read_i32::<BE>().map_err(|_| Error::InvalidTag)?;
|
||||
let mut bytes = Vec::new();
|
||||
for _ in 0..length {
|
||||
bytes.push(stream.read_i8().map_err(|_| Error::InvalidTag)?);
|
||||
}
|
||||
Tag::ByteArray(bytes)
|
||||
}
|
||||
// A length-prefixed modified UTF-8 string. The prefix is an
|
||||
// unsigned short (thus 2 bytes) signifying the length of the
|
||||
// string in bytes
|
||||
8 => {
|
||||
let length = stream.read_u16::<BE>().map_err(|_| Error::InvalidTag)?;
|
||||
let mut bytes = Vec::new();
|
||||
for _ in 0..length {
|
||||
bytes.push(stream.read_u8().map_err(|_| Error::InvalidTag)?);
|
||||
}
|
||||
Tag::String(String::from_utf8(bytes).map_err(|_| Error::InvalidTag)?)
|
||||
}
|
||||
// A list of nameless tags, all of the same type. The list is
|
||||
// prefixed with the Type ID of the items it contains (thus 1
|
||||
// byte), and the length of the list as a signed integer (a further
|
||||
// 4 bytes). If the length of the list is 0 or negative, the type
|
||||
// may be 0 (TAG_End) but otherwise it must be any other type. (The
|
||||
// notchian implementation uses TAG_End in that situation, but
|
||||
// another reference implementation by Mojang uses 1 instead;
|
||||
// parsers should accept any type if the length is <= 0).
|
||||
9 => {
|
||||
let type_id = stream.read_u8().map_err(|_| Error::InvalidTag)?;
|
||||
let length = stream.read_i32::<BE>().map_err(|_| Error::InvalidTag)?;
|
||||
let mut list = Vec::new();
|
||||
for _ in 0..length {
|
||||
list.push(Tag::read_known(stream, type_id)?);
|
||||
}
|
||||
Tag::List(list)
|
||||
}
|
||||
// Effectively a list of a named tags. Order is not guaranteed.
|
||||
10 => {
|
||||
println!("reading compound {{");
|
||||
let mut map = HashMap::new();
|
||||
loop {
|
||||
let tag_id = stream.read_u8().unwrap_or(0);
|
||||
println!("compound tag id: {}", tag_id);
|
||||
if tag_id == 0 {
|
||||
break;
|
||||
}
|
||||
let name = match Tag::read_known(stream, 8)? {
|
||||
Tag::String(name) => name,
|
||||
_ => panic!("Expected a string tag"),
|
||||
};
|
||||
println!("compound name: {}", name);
|
||||
let tag = Tag::read_known(stream, tag_id).map_err(|_| Error::InvalidTag)?;
|
||||
println!("aight read tag: {:?}", tag);
|
||||
map.insert(name, tag);
|
||||
}
|
||||
println!("}} compound map: {:?}", map);
|
||||
Tag::Compound(map)
|
||||
}
|
||||
// A length-prefixed array of signed integers. The prefix is a
|
||||
// signed integer (thus 4 bytes) and indicates the number of 4 byte
|
||||
// integers.
|
||||
11 => {
|
||||
let length = stream.read_i32::<BE>().map_err(|_| Error::InvalidTag)?;
|
||||
let mut ints = Vec::new();
|
||||
for _ in 0..length {
|
||||
ints.push(stream.read_i32::<BE>().map_err(|_| Error::InvalidTag)?);
|
||||
}
|
||||
Tag::IntArray(ints)
|
||||
}
|
||||
// A length-prefixed array of signed longs. The prefix is a signed
|
||||
// integer (thus 4 bytes) and indicates the number of 8 byte longs.
|
||||
12 => {
|
||||
let length = stream.read_i32::<BE>().map_err(|_| Error::InvalidTag)?;
|
||||
let mut longs = Vec::new();
|
||||
for _ in 0..length {
|
||||
longs.push(stream.read_i64::<BE>().map_err(|_| Error::InvalidTag)?);
|
||||
}
|
||||
Tag::LongArray(longs)
|
||||
}
|
||||
_ => return Err(Error::InvalidTagType(id)),
|
||||
};
|
||||
Ok(tag)
|
||||
}
|
||||
|
||||
pub fn read(stream: &mut impl Read) -> Result<Tag, Error> {
|
||||
// default to compound tag
|
||||
Tag::read_known(stream, 10)
|
||||
}
|
||||
}
|
5
azalea-nbt/src/error.rs
Normal file
5
azalea-nbt/src/error.rs
Normal file
|
@ -0,0 +1,5 @@
|
|||
#[derive(Debug)]
|
||||
pub enum Error {
|
||||
InvalidTagType(u8),
|
||||
InvalidTag,
|
||||
}
|
6
azalea-nbt/src/lib.rs
Normal file
6
azalea-nbt/src/lib.rs
Normal file
|
@ -0,0 +1,6 @@
|
|||
mod decode;
|
||||
mod error;
|
||||
mod tag;
|
||||
|
||||
pub use error::Error;
|
||||
pub use tag::Tag;
|
18
azalea-nbt/src/tag.rs
Normal file
18
azalea-nbt/src/tag.rs
Normal file
|
@ -0,0 +1,18 @@
|
|||
use std::collections::HashMap;
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
pub enum Tag {
|
||||
End, // 0
|
||||
Byte(i8), // 1
|
||||
Short(i16), // 2
|
||||
Int(i32), // 3
|
||||
Long(i64), // 4
|
||||
Float(f32), // 5
|
||||
Double(f64), // 6
|
||||
ByteArray(Vec<i8>), // 7
|
||||
String(String), // 8
|
||||
List(Vec<Tag>), // 9
|
||||
Compound(HashMap<String, Tag>), // 10
|
||||
IntArray(Vec<i32>), // 11
|
||||
LongArray(Vec<i64>), // 12
|
||||
}
|
BIN
azalea-nbt/tests/bigtest.nbt
Normal file
BIN
azalea-nbt/tests/bigtest.nbt
Normal file
Binary file not shown.
19
azalea-nbt/tests/decode.rs
Normal file
19
azalea-nbt/tests/decode.rs
Normal file
|
@ -0,0 +1,19 @@
|
|||
use azalea_nbt::Tag;
|
||||
use std::collections::HashMap;
|
||||
|
||||
#[test]
|
||||
fn test_hello_world() {
|
||||
// read hello_world.nbt
|
||||
let mut file = std::fs::File::open("tests/hello_world.nbt").unwrap();
|
||||
let tag = Tag::read(&mut file).unwrap();
|
||||
assert_eq!(
|
||||
tag,
|
||||
Tag::Compound(HashMap::from_iter(vec![(
|
||||
"hello world".to_string(),
|
||||
Tag::Compound(HashMap::from_iter(vec![(
|
||||
"name".to_string(),
|
||||
Tag::String("Bananrama".to_string()),
|
||||
)]))
|
||||
)]))
|
||||
);
|
||||
}
|
BIN
azalea-nbt/tests/hello_world.nbt
Normal file
BIN
azalea-nbt/tests/hello_world.nbt
Normal file
Binary file not shown.
Loading…
Reference in a new issue