├── .gitignore ├── tests ├── small1.nbt ├── big1.nbt ├── arrays.nbt ├── level.dat ├── small2.nbt ├── small3.nbt ├── small4.nbt ├── complex_player.dat ├── simple_player.dat ├── serde_errors.rs ├── filetests.rs ├── data.rs.in └── serde_basics.rs ├── scripts ├── id_rsa.enc └── travis-doc-upload.cfg ├── .travis.yml ├── Cargo.toml ├── LICENSE ├── src ├── lib.rs ├── error.rs ├── raw.rs ├── macros.rs ├── blob.rs ├── de.rs ├── value.rs ├── tests.rs └── ser.rs ├── examples └── nbtprint.rs ├── README.md ├── benches └── filetests.rs └── NEWS.md /.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | Cargo.lock 3 | -------------------------------------------------------------------------------- /tests/small1.nbt: -------------------------------------------------------------------------------- 1 | 2 | hello worldname Bananrama -------------------------------------------------------------------------------- /tests/big1.nbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/hematite_nbt/HEAD/tests/big1.nbt -------------------------------------------------------------------------------- /tests/arrays.nbt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/hematite_nbt/HEAD/tests/arrays.nbt -------------------------------------------------------------------------------- /tests/level.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/hematite_nbt/HEAD/tests/level.dat -------------------------------------------------------------------------------- /tests/small2.nbt: -------------------------------------------------------------------------------- 1 | 2 | hello world 3 | aaa12"3"3D 4 | bbb12"3"3D -------------------------------------------------------------------------------- /tests/small3.nbt: -------------------------------------------------------------------------------- 1 | 2 | aaa bbb 3 | namewololoccc"3Dnamewololoccc"3D -------------------------------------------------------------------------------- /scripts/id_rsa.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/hematite_nbt/HEAD/scripts/id_rsa.enc -------------------------------------------------------------------------------- /tests/small4.nbt: -------------------------------------------------------------------------------- 1 | 2 | Level 3 | c1aaabbb"ccc3dddD 4 | c2aaabbb"ccc3dddD -------------------------------------------------------------------------------- /tests/complex_player.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/hematite_nbt/HEAD/tests/complex_player.dat -------------------------------------------------------------------------------- /tests/simple_player.dat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/PistonDevelopers/hematite_nbt/HEAD/tests/simple_player.dat -------------------------------------------------------------------------------- /scripts/travis-doc-upload.cfg: -------------------------------------------------------------------------------- 1 | PROJECT_NAME=hematite_nbt 2 | DOCS_REPO=PistonDevelopers/docs.git 3 | SSH_KEY_TRAVIS_ID=f2abd1c95733 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: rust 2 | notifications: 3 | irc: "irc.mozilla.org#hematite" 4 | script: 5 | - rustup component add rustfmt 6 | - cargo fmt -- --check 7 | - cargo test -v 8 | - cargo doc -v 9 | after_success: 10 | - curl http://docs.piston.rs/travis-doc-upload.sh | sh 11 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "hematite-nbt" 3 | description = "A full-featured library for working with Minecraft's Named Binary Tag (NBT) file format, including Serde support." 4 | documentation = "https://docs.rs/hematite-nbt/" 5 | repository = "https://github.com/PistonDevelopers/hematite_nbt" 6 | readme = "README.md" 7 | license = "MIT" 8 | keywords = ["nbt", "minecraft", "serde", "serialization"] 9 | version = "0.5.2" 10 | authors = [ 11 | "Aaron Jacobs ", 12 | "Fenhl ", 13 | "Carlos Cobo " 14 | ] 15 | 16 | [features] 17 | default = ["serde"] 18 | preserve_order = ["indexmap"] 19 | 20 | [lib] 21 | name = "nbt" 22 | path = "src/lib.rs" 23 | bench = false 24 | 25 | [dependencies] 26 | byteorder = "1.0.0" 27 | cesu8 = "1.1.0" 28 | flate2 = "1.0.16" 29 | indexmap = { version = "1.4", optional = true, features = ["serde-1"] } 30 | serde = { version = "1.0", optional = true, features = ["derive"] } 31 | 32 | [dev-dependencies] 33 | criterion = "0.3" 34 | serde_derive = "1.0" 35 | serde_json = "1.0" 36 | 37 | [[bench]] 38 | name = "filetests" 39 | harness = false 40 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 PistonDevelopers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /src/lib.rs: -------------------------------------------------------------------------------- 1 | //! MC Named Binary Tag type. 2 | 3 | extern crate byteorder; 4 | extern crate cesu8; 5 | extern crate flate2; 6 | 7 | /* Re-export the core API from submodules. */ 8 | pub use blob::Blob; 9 | pub use error::{Error, Result}; 10 | pub use value::Value; 11 | 12 | #[cfg(feature = "preserve_order")] 13 | extern crate indexmap; 14 | #[cfg(feature = "preserve_order")] 15 | pub use indexmap::IndexMap as Map; 16 | #[cfg(not(feature = "preserve_order"))] 17 | pub use std::collections::HashMap as Map; 18 | 19 | #[cfg(feature = "serde")] 20 | #[doc(inline)] 21 | pub use de::{from_gzip_reader, from_reader, from_zlib_reader}; 22 | #[cfg(feature = "serde")] 23 | #[doc(inline)] 24 | pub use ser::{i32_array, i64_array, i8_array}; 25 | #[cfg(feature = "serde")] 26 | #[doc(inline)] 27 | pub use ser::{to_gzip_writer, to_writer, to_zlib_writer}; 28 | 29 | mod blob; 30 | mod error; 31 | mod raw; 32 | mod value; 33 | 34 | #[cfg(feature = "serde")] 35 | #[macro_use] 36 | extern crate serde; 37 | 38 | #[cfg(feature = "serde")] 39 | #[macro_use] 40 | mod macros; 41 | #[cfg(feature = "serde")] 42 | pub mod de; 43 | #[cfg(feature = "serde")] 44 | pub mod ser; 45 | 46 | #[cfg(test)] 47 | mod tests; 48 | -------------------------------------------------------------------------------- /examples/nbtprint.rs: -------------------------------------------------------------------------------- 1 | extern crate nbt; 2 | extern crate serde_json; 3 | 4 | use std::env; 5 | use std::fs; 6 | use std::process::exit; 7 | 8 | use nbt::Blob; 9 | use nbt::Result; 10 | 11 | fn run() -> Result<()> { 12 | let args: Vec = env::args().collect(); 13 | if let Some(arg) = args.into_iter().skip(1).take(1).next() { 14 | let mut file = fs::File::open(&arg)?; 15 | println!( 16 | "================================= NBT Contents =================================" 17 | ); 18 | let blob = Blob::from_reader(&mut file)?; 19 | println!("{}", blob); 20 | println!( 21 | "============================== JSON Representation =============================" 22 | ); 23 | match serde_json::to_string_pretty(&blob) { 24 | Ok(json) => println!("{}", json), 25 | Err(e) => { 26 | eprintln!("error: {}", e); 27 | exit(1) 28 | } 29 | } 30 | Ok(()) 31 | } else { 32 | eprintln!("error: a filename is required."); 33 | exit(1) 34 | } 35 | } 36 | 37 | fn main() { 38 | if let Err(err) = run() { 39 | eprintln!("error: {}", err); 40 | exit(1) 41 | }; 42 | } 43 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hematite_nbt [![hematite_nbt at crates.io](https://img.shields.io/crates/v/hematite_nbt.svg)](https://crates.io/crates/hematite_nbt) [![hematite_nbt at docs.rs](https://docs.rs/hematite-nbt/badge.svg)](https://docs.rs/hematite-nbt) [![Build Status](https://travis-ci.org/PistonDevelopers/hematite_nbt.svg?branch=master)](https://travis-ci.org/PistonDevelopers/hematite_nbt) 2 | 3 | This repository contains the [Hematite project](http://hematite.piston.rs/)'s 4 | standalone `nbt` crate for working with Minecraft's [Named Binary Tag](https://minecraft.gamepedia.com/NBT_format) 5 | (NBT) format. 6 | 7 | This is not the only NBT-related crate available, but it has some notable 8 | features: 9 | 10 | * Full support for serializing and deserializing types via [Serde](https://serde.rs/). 11 | This means that you can read and write the NBT binary format of any struct 12 | annotated with the standard `#[derive(Serialize, Deserialize)]` traits 13 | (provided it actually has a valid NBT representation). 14 | 15 | * An API that attempts to differentiate between complete and partial NBT objects 16 | via `nbt::Blob` and `nbt::Value`. Only complete objects can be serialized. 17 | 18 | * Support for the `TAG_Long_Array` data introduced in Minecraft 1.12. 19 | 20 | * Support for the modified UTF-8 encoding used by the vanilla Minecraft client. 21 | 22 | ## License 23 | 24 | Licensed under the terms of the MIT license. 25 | -------------------------------------------------------------------------------- /benches/filetests.rs: -------------------------------------------------------------------------------- 1 | //! Crate for testing whether the deserialize codegen is capable of handling the 2 | //! sample NBT files in the test/ directory, which include real 3 | //! Minecraft-generated files. 4 | 5 | extern crate criterion; 6 | #[macro_use] 7 | extern crate serde_derive; 8 | extern crate serde; 9 | 10 | extern crate nbt; 11 | 12 | use std::fs::File; 13 | use std::io::{self, Read, Seek, SeekFrom}; 14 | 15 | use criterion::{criterion_group, criterion_main, Criterion, Throughput}; 16 | 17 | use nbt::de::from_gzip_reader; 18 | use nbt::ser::to_writer; 19 | 20 | mod data { 21 | include!("../tests/data.rs.in"); 22 | } 23 | 24 | fn bench_serialize(filename: &str, c: &mut Criterion) 25 | where 26 | T: serde::de::DeserializeOwned + serde::ser::Serialize, 27 | { 28 | let mut file = File::open(filename).unwrap(); 29 | let mut contents = Vec::new(); 30 | file.read_to_end(&mut contents).unwrap(); 31 | let mut src = std::io::Cursor::new(&contents[..]); 32 | file.seek(SeekFrom::Start(0)).unwrap(); 33 | let nbt_struct: T = from_gzip_reader(&mut file).unwrap(); 34 | file.seek(SeekFrom::Start(0)).unwrap(); 35 | let nbt_blob = nbt::Blob::from_gzip_reader(&mut file).unwrap(); 36 | 37 | let mut group = c.benchmark_group(filename); 38 | group.throughput(Throughput::Bytes(contents.len() as u64)); 39 | group.bench_function("Deserialize As Struct", |b| { 40 | b.iter(|| { 41 | src.seek(SeekFrom::Start(0)).unwrap(); 42 | let _: T = from_gzip_reader(&mut src).unwrap(); 43 | }) 44 | }); 45 | group.bench_function("Deserialize As Blob", |b| { 46 | b.iter(|| { 47 | src.seek(SeekFrom::Start(0)).unwrap(); 48 | nbt::Blob::from_gzip_reader(&mut src).unwrap(); 49 | }) 50 | }); 51 | group.bench_function("Serialize As Struct", |b| { 52 | b.iter(|| { 53 | to_writer(&mut io::sink(), &nbt_struct, None).unwrap(); 54 | }) 55 | }); 56 | group.bench_function("Serialize As Blob", |b| { 57 | b.iter(|| { 58 | nbt_blob.to_writer(&mut io::sink()).unwrap(); 59 | }) 60 | }); 61 | group.finish(); 62 | } 63 | 64 | fn bench(c: &mut Criterion) { 65 | bench_serialize::("tests/big1.nbt", c); 66 | bench_serialize::("tests/simple_player.dat", c); 67 | bench_serialize::("tests/complex_player.dat", c); 68 | bench_serialize::("tests/level.dat", c); 69 | } 70 | 71 | criterion_group!(benches, bench); 72 | criterion_main!(benches); 73 | -------------------------------------------------------------------------------- /tests/serde_errors.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate serde_derive; 3 | extern crate serde; 4 | 5 | extern crate nbt; 6 | 7 | use nbt::de::from_reader; 8 | use nbt::ser::to_writer; 9 | use nbt::{Error, Result}; 10 | 11 | #[test] 12 | fn no_root_compound() { 13 | let nbt: i8 = 100; 14 | 15 | let mut dst = Vec::new(); 16 | let write = to_writer(&mut dst, &nbt, None); 17 | 18 | assert!(write.is_err()); 19 | match write.unwrap_err() { 20 | Error::NoRootCompound => (), 21 | _ => panic!("encountered an unexpected error"), 22 | } 23 | } 24 | 25 | #[derive(Debug, Deserialize)] 26 | struct ByteNbt { 27 | data: i8, 28 | } 29 | 30 | #[test] 31 | fn incomplete_nbt() { 32 | #[rustfmt::skip] 33 | let bytes = vec![ 34 | 0x0a, 35 | 0x00, 0x00, 36 | 0x01, 37 | 0x00, 0x04, 38 | 0x64, 0x61, 0x74, 0x61, 39 | 0x01 40 | ]; 41 | 42 | let read: Result = from_reader(&bytes[..]); 43 | 44 | assert!(read.is_err()); 45 | match read.unwrap_err() { 46 | Error::IncompleteNbtValue => (), 47 | _ => panic!("encountered an unexpected error"), 48 | } 49 | } 50 | 51 | #[test] 52 | fn unknown_tag() { 53 | #[rustfmt::skip] 54 | let bytes = vec![ 55 | 0x0a, 56 | 0x00, 0x00, 57 | 0x0f, 58 | 0x00, 0x04, 59 | 0x64, 0x61, 0x74, 0x61, 60 | 0x01, 61 | 0x00 62 | ]; 63 | 64 | let read: Result = from_reader(&bytes[..]); 65 | 66 | assert!(read.is_err()); 67 | match read.unwrap_err() { 68 | Error::InvalidTypeId(t) => assert_eq!(t, 0x0f), 69 | _ => panic!("encountered an unexpected error"), 70 | } 71 | } 72 | 73 | #[test] 74 | fn deserialized_wrong_type() { 75 | #[rustfmt::skip] 76 | let bytes = vec![ 77 | 0x0a, 78 | 0x00, 0x00, 79 | 0x08, 80 | 0x00, 0x04, 81 | 0x64, 0x61, 0x74, 0x61, 82 | 0x00, 0x00, 83 | 0x00 84 | ]; 85 | 86 | let read: Result = from_reader(&bytes[..]); 87 | 88 | assert!(read.is_err()); 89 | match read.unwrap_err() { 90 | Error::Serde(msg) => assert_eq!(&msg, "invalid type: string \"\", expected i8"), 91 | _ => panic!("encountered an unexpected error"), 92 | } 93 | } 94 | 95 | #[derive(Debug, Deserialize)] 96 | struct BoolNbt { 97 | data: bool, 98 | } 99 | 100 | #[test] 101 | fn non_boolean_byte() { 102 | #[rustfmt::skip] 103 | let bytes = vec![ 104 | 0x0a, 105 | 0x00, 0x00, 106 | 0x01, 107 | 0x00, 0x04, 108 | 0x64, 0x61, 0x74, 0x61, 109 | 0x02, 110 | 0x00 111 | ]; 112 | 113 | let read: Result = from_reader(&bytes[..]); 114 | 115 | assert!(read.is_err()); 116 | match read.unwrap_err() { 117 | Error::NonBooleanByte(v) => assert_eq!(v, 0x02), 118 | _ => panic!("encountered an unexpected error"), 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /NEWS.md: -------------------------------------------------------------------------------- 1 | # hematite_nbt 0.5.2 2 | 3 | * Enum variants can now be serialized by name. (#62 by @Freax13) 4 | 5 | # hematite_nbt 0.5.1 6 | 7 | * Fixes a performance regression in array serialization. (#61 by @caelunshun) 8 | 9 | * Fixes an incompatibility with `serde` versions 1.0.119 and later. (#60 by 10 | @Iaiao) 11 | 12 | # hematite_nbt 0.5.0 13 | 14 | ## New Features 15 | 16 | * Compiling with the `preserve_order` feature will use an `IndexMap` instead of 17 | a `HashMap` for compound tags, which preserves insertion order when 18 | serializing. (#54 by @samlich) 19 | 20 | * Structs can now be explicitly told to serialize as byte/int/long arrays 21 | instead of a list under Serde by using `serialize_with` and the new 22 | `i8_array()`, `i32_array()`, and `i64_array()` functions. (#51, #52, and #53 23 | by @Schuwi). For example: 24 | 25 | ``` 26 | #[derive(Serialize)] 27 | struct MyStruct { 28 | #[serde(serialize_with="hematite_nbt::i8_array")] 29 | byte_array: Vec, 30 | } 31 | ``` 32 | 33 | * `Blob` now has a `len_bytes()` method to compute the expected length when 34 | serialized, restoring functionality removed in #36. It turns out to be helpful 35 | to know the length when writing NBT payloads into network packets. (#48 by 36 | @ejmount) 37 | 38 | * `Blob` now has a `get()` method to get values by name; previously this was 39 | only possible via indexing, which would panic if the key was absent. (#44 by 40 | @vlakreeh) 41 | 42 | * `Blob` now implements `Default::default()`, as suggested by Clippy. 43 | 44 | ## Bug Fixes and Other Improvements 45 | 46 | * The `flate2` dependency has been bumped to enable compilation for WASM. (#56 47 | by @oOBoomberOo) 48 | 49 | * The use of various deprecated APIs in the Rust standard library has been 50 | fixed, so consumers of this crate will no longer see build warnings. (#49 by 51 | @atheriel) 52 | 53 | * `UnrepresentableType` will now correctly show the type in the error message, 54 | instead of the literal string `$type`. (#47 by @caelunshun) 55 | 56 | * The benchmark suite has been rewritten to use Criterion, so it should compile 57 | with stable Rust. However, it's not clear that the black-box benchmarks are 58 | actually working properly at this time. 59 | 60 | * The project now uses and enforces `rustfmt` rules. 61 | 62 | # hematite_nbt 0.4.1 63 | 64 | * Strings are now encoded and decoded using [Modified UTF-8](https://en.wikipedia.org/wiki/UTF-8#Modified_UTF-8), 65 | matching the behaviour of the vanilla Minecraft client. This should only 66 | affect strings with codepoints outside of the Basic Multilingual Plane, which 67 | are likely quite rare in the wild. Note that we permissively handle true UTF-8 68 | input, but always write MUTF-8 output. (#39) 69 | 70 | * Serde-based deserialization now supports `TAG_LongArray`. (#41 by @caelunshun) 71 | 72 | * Empty lists are now written with `TAG_End` as the list type instead of 73 | `TAG_Byte`. This matches the behaviour of the vanilla Minecraft client, 74 | although it should not in theory affect well-behaved NBT parsers. 75 | 76 | * Fixes Serde-based serialization of empty lists. 77 | 78 | * Updates the project `README` and adds a `NEWS` file. 79 | 80 | # hematite_nbt 0.4.0 81 | 82 | ## Breaking Changes 83 | 84 | * `Blob::new()` no longer takes a name parameter; use `Blob::named()` instead. 85 | (#36) 86 | 87 | * Implementation details on `Blob` and `Value` including `read_header()`, 88 | `write_header()`, and `len()` are no longer public or have been removed. (#36) 89 | 90 | * `Value::write()` is now `Value::to_writer()`, matching the rest of the API. 91 | Similarly, `Blob::(to|from)_(gzip|zlib)` methods now include a 92 | `(reader|writer)` prefix. (#36) 93 | 94 | * The unfinished `macros` module and the associated `NbtFmt` trait have been 95 | removed. These are superceded by Serde support. (#25) 96 | 97 | ## New Features 98 | 99 | * Support for (de)serializing Rust types (including `Blob`) to NBT using the 100 | Serde framework. This is an optional feature but enabled by default. (#24, 101 | #30, #31) 102 | 103 | * Support for `TAG_LongArray` via `Value::LongArray`. (#28 by @williewillus) 104 | 105 | * Improved printing support for `Blob` and `Value` and added an example program 106 | for printing out NBT files. 107 | 108 | # hematite_nbt 0.3.0 109 | 110 | * Bumps the `byteorder` dependency to 1.0.0 and makes associated changes. 111 | -------------------------------------------------------------------------------- /src/error.rs: -------------------------------------------------------------------------------- 1 | use std::error::Error as StdError; 2 | use std::fmt; 3 | use std::io; 4 | use std::io::ErrorKind::InvalidInput; 5 | use std::result::Result as StdResult; 6 | 7 | #[cfg(feature = "serde")] 8 | use serde; 9 | 10 | /// A convenient alias type for results when reading/writing the Named Binary 11 | /// Tag format. 12 | pub type Result = StdResult; 13 | 14 | /// Errors that may be encountered when constructing, parsing, or encoding 15 | /// `NbtValue` and `NbtBlob` objects. 16 | /// 17 | /// `Error`s can be seamlessly converted to more general `io::Error` objects 18 | /// using `std::convert::From::from()`. 19 | #[derive(Debug)] 20 | pub enum Error { 21 | /// Wraps errors emitted by methods during I/O operations. 22 | IoError(io::Error), 23 | /// Wraps errors emitted during (de-)serialization with `serde`. 24 | #[cfg(feature = "serde")] 25 | Serde(String), 26 | /// An error for when an unknown type ID is encountered in decoding NBT 27 | /// binary representations. Includes the ID in question. 28 | InvalidTypeId(u8), 29 | /// An error emitted when trying to create `NbtBlob`s with incorrect lists. 30 | HeterogeneousList, 31 | /// An error for when NBT binary representations do not begin with an 32 | /// `NbtValue::Compound`. 33 | NoRootCompound, 34 | /// An error for when NBT binary representations contain invalid UTF-8 35 | /// strings. 36 | InvalidUtf8, 37 | /// An error for when NBT binary representations are missing end tags, 38 | /// contain fewer bytes than advertised, or are otherwise incomplete. 39 | IncompleteNbtValue, 40 | /// An error encountered when parsing NBT binary representations, where 41 | /// deserialization encounters a different tag than expected. 42 | TagMismatch(u8, u8), 43 | /// An error encountered when parsing NBT binary representations, where 44 | /// deserialization encounters a field name it is not expecting. 45 | UnexpectedField(String), 46 | /// An error encountered when deserializing a boolean from an invalid byte. 47 | NonBooleanByte(i8), 48 | /// An error encountered when serializing a Rust type with no meaningful NBT 49 | /// representation. 50 | UnrepresentableType(&'static str), 51 | /// An error encountered when trying to (de)serialize a map key with a 52 | /// non-string type. 53 | NonStringMapKey, 54 | } 55 | 56 | impl fmt::Display for Error { 57 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 58 | match self { 59 | &Error::IoError(ref e) => e.fmt(f), 60 | #[cfg(feature = "serde")] 61 | &Error::Serde(ref msg) => write!(f, "{}", msg), 62 | &Error::InvalidTypeId(t) => write!(f, "invalid NBT tag byte: '{}'", t), 63 | Error::HeterogeneousList => write!(f, "values in NBT Lists must be homogeneous"), 64 | Error::NoRootCompound => write!(f, "the root value must be Compound-like (tag = 0x0a)"), 65 | Error::InvalidUtf8 => write!(f, "a string is not valid UTF-8"), 66 | Error::IncompleteNbtValue => write!(f, "data does not represent a complete NbtValue"), 67 | &Error::TagMismatch(a, b) => { 68 | write!(f, "encountered NBT tag '{}' but expected '{}'", a, b) 69 | } 70 | &Error::NonBooleanByte(b) => { 71 | write!(f, "encountered a byte value '{}' inside a boolean", b) 72 | } 73 | &Error::UnexpectedField(ref name) => { 74 | write!(f, "encountered an unexpected field '{}'", name) 75 | } 76 | &Error::UnrepresentableType(ref name) => write!( 77 | f, 78 | "encountered type '{}', which has no meaningful NBT representation", 79 | name 80 | ), 81 | Error::NonStringMapKey => write!(f, "encountered a non-string map key"), 82 | } 83 | } 84 | } 85 | 86 | impl StdError for Error { 87 | fn source(&self) -> Option<&(dyn StdError + 'static)> { 88 | match *self { 89 | Error::IoError(ref e) => e.source(), 90 | _ => None, 91 | } 92 | } 93 | } 94 | 95 | // Implement PartialEq manually, since std::io::Error is not PartialEq. 96 | impl PartialEq for Error { 97 | fn eq(&self, other: &Error) -> bool { 98 | use Error::{ 99 | HeterogeneousList, IncompleteNbtValue, InvalidTypeId, InvalidUtf8, IoError, 100 | NoRootCompound, NonBooleanByte, TagMismatch, UnexpectedField, UnrepresentableType, 101 | }; 102 | 103 | match (self, other) { 104 | (&IoError(_), &IoError(_)) => true, 105 | #[cfg(feature = "serde")] 106 | (&Error::Serde(_), &Error::Serde(_)) => true, 107 | (&InvalidTypeId(a), &InvalidTypeId(b)) => a == b, 108 | (&HeterogeneousList, &HeterogeneousList) => true, 109 | (&NoRootCompound, &NoRootCompound) => true, 110 | (&InvalidUtf8, &InvalidUtf8) => true, 111 | (&IncompleteNbtValue, &IncompleteNbtValue) => true, 112 | (&TagMismatch(a, b), &TagMismatch(c, d)) => a == c && b == d, 113 | (&UnexpectedField(ref a), &UnexpectedField(ref b)) => a == b, 114 | (&NonBooleanByte(a), &NonBooleanByte(b)) => a == b, 115 | (&UnrepresentableType(ref a), &UnrepresentableType(ref b)) => a == b, 116 | _ => false, 117 | } 118 | } 119 | } 120 | 121 | impl From for Error { 122 | fn from(e: io::Error) -> Error { 123 | use std::io::ErrorKind; 124 | 125 | if e.kind() == ErrorKind::UnexpectedEof { 126 | return Error::IncompleteNbtValue; 127 | } 128 | Error::IoError(e) 129 | } 130 | } 131 | 132 | impl From for Error { 133 | fn from(_: cesu8::Cesu8DecodingError) -> Error { 134 | Error::InvalidUtf8 135 | } 136 | } 137 | 138 | impl From for io::Error { 139 | fn from(e: Error) -> io::Error { 140 | match e { 141 | Error::IoError(e) => e, 142 | other => io::Error::new(InvalidInput, other), 143 | } 144 | } 145 | } 146 | 147 | #[cfg(feature = "serde")] 148 | impl serde::ser::Error for Error { 149 | fn custom(msg: T) -> Error { 150 | Error::Serde(msg.to_string()) 151 | } 152 | } 153 | 154 | #[cfg(feature = "serde")] 155 | impl serde::de::Error for Error { 156 | fn custom(msg: T) -> Error { 157 | Error::Serde(msg.to_string()) 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/raw.rs: -------------------------------------------------------------------------------- 1 | //! Primitive functions for serializing and deserializing NBT data. 2 | 3 | use std::io; 4 | 5 | use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; 6 | use cesu8::{from_java_cesu8, to_java_cesu8}; 7 | 8 | use error::{Error, Result}; 9 | 10 | /// A convenience function for closing NBT format objects. 11 | /// 12 | /// This function writes a single `0x00` byte to the `io::Write` destination, 13 | /// which in the NBT format indicates that an open Compound is now closed. 14 | pub fn close_nbt(dst: &mut W) -> Result<()> 15 | where 16 | W: io::Write, 17 | { 18 | dst.write_u8(0x00).map_err(From::from) 19 | } 20 | 21 | #[inline] 22 | pub fn write_bare_byte(dst: &mut W, value: i8) -> Result<()> 23 | where 24 | W: io::Write, 25 | { 26 | dst.write_i8(value).map_err(From::from) 27 | } 28 | 29 | #[inline] 30 | pub fn write_bare_short(dst: &mut W, value: i16) -> Result<()> 31 | where 32 | W: io::Write, 33 | { 34 | dst.write_i16::(value).map_err(From::from) 35 | } 36 | 37 | #[inline] 38 | pub fn write_bare_int(dst: &mut W, value: i32) -> Result<()> 39 | where 40 | W: io::Write, 41 | { 42 | dst.write_i32::(value).map_err(From::from) 43 | } 44 | 45 | #[inline] 46 | pub fn write_bare_long(dst: &mut W, value: i64) -> Result<()> 47 | where 48 | W: io::Write, 49 | { 50 | dst.write_i64::(value).map_err(From::from) 51 | } 52 | 53 | #[inline] 54 | pub fn write_bare_float(dst: &mut W, value: f32) -> Result<()> 55 | where 56 | W: io::Write, 57 | { 58 | dst.write_f32::(value).map_err(From::from) 59 | } 60 | 61 | #[inline] 62 | pub fn write_bare_double(dst: &mut W, value: f64) -> Result<()> 63 | where 64 | W: io::Write, 65 | { 66 | dst.write_f64::(value).map_err(From::from) 67 | } 68 | 69 | #[inline] 70 | pub fn write_bare_byte_array(dst: &mut W, value: &[i8]) -> Result<()> 71 | where 72 | W: io::Write, 73 | { 74 | dst.write_i32::(value.len() as i32)?; 75 | for &v in value { 76 | dst.write_i8(v)?; 77 | } 78 | Ok(()) 79 | } 80 | 81 | #[inline] 82 | pub fn write_bare_int_array(dst: &mut W, value: &[i32]) -> Result<()> 83 | where 84 | W: io::Write, 85 | { 86 | dst.write_i32::(value.len() as i32)?; 87 | for &v in value { 88 | dst.write_i32::(v)?; 89 | } 90 | Ok(()) 91 | } 92 | 93 | #[inline] 94 | pub fn write_bare_long_array(dst: &mut W, value: &[i64]) -> Result<()> 95 | where 96 | W: io::Write, 97 | { 98 | dst.write_i32::(value.len() as i32)?; 99 | for &v in value { 100 | dst.write_i64::(v)?; 101 | } 102 | Ok(()) 103 | } 104 | 105 | #[inline] 106 | pub fn write_bare_string(dst: &mut W, value: &str) -> Result<()> 107 | where 108 | W: io::Write, 109 | { 110 | let encoded = to_java_cesu8(value); 111 | dst.write_u16::(encoded.len() as u16)?; 112 | dst.write_all(&encoded).map_err(From::from) 113 | } 114 | 115 | /// Extracts the next header (tag and name) from an NBT format source. 116 | /// 117 | /// This function will also return the `TAG_End` byte and an empty name if it 118 | /// encounters it. 119 | pub fn emit_next_header(src: &mut R) -> Result<(u8, String)> 120 | where 121 | R: io::Read, 122 | { 123 | let tag = src.read_u8()?; 124 | 125 | match tag { 126 | 0x00 => Ok((tag, "".to_string())), 127 | _ => { 128 | let name = read_bare_string(src)?; 129 | Ok((tag, name)) 130 | } 131 | } 132 | } 133 | 134 | #[inline] 135 | pub fn read_bare_byte(src: &mut R) -> Result 136 | where 137 | R: io::Read, 138 | { 139 | src.read_i8().map_err(From::from) 140 | } 141 | 142 | #[inline] 143 | pub fn read_bare_short(src: &mut R) -> Result 144 | where 145 | R: io::Read, 146 | { 147 | src.read_i16::().map_err(From::from) 148 | } 149 | 150 | #[inline] 151 | pub fn read_bare_int(src: &mut R) -> Result 152 | where 153 | R: io::Read, 154 | { 155 | src.read_i32::().map_err(From::from) 156 | } 157 | 158 | #[inline] 159 | pub fn read_bare_long(src: &mut R) -> Result 160 | where 161 | R: io::Read, 162 | { 163 | src.read_i64::().map_err(From::from) 164 | } 165 | 166 | #[inline] 167 | pub fn read_bare_float(src: &mut R) -> Result 168 | where 169 | R: io::Read, 170 | { 171 | src.read_f32::().map_err(From::from) 172 | } 173 | 174 | #[inline] 175 | pub fn read_bare_double(src: &mut R) -> Result 176 | where 177 | R: io::Read, 178 | { 179 | src.read_f64::().map_err(From::from) 180 | } 181 | 182 | #[inline] 183 | pub fn read_bare_byte_array(src: &mut R) -> Result> 184 | where 185 | R: io::Read, 186 | { 187 | // FIXME: Is there a way to return [u8; len]? 188 | let len = src.read_i32::()? as usize; 189 | let mut buf = Vec::with_capacity(len); 190 | // FIXME: Test performance vs transmute. 191 | for _ in 0..len { 192 | buf.push(src.read_i8()?); 193 | } 194 | Ok(buf) 195 | } 196 | 197 | #[inline] 198 | pub fn read_bare_int_array(src: &mut R) -> Result> 199 | where 200 | R: io::Read, 201 | { 202 | // FIXME: Is there a way to return [i32; len]? 203 | let len = src.read_i32::()? as usize; 204 | let mut buf = Vec::with_capacity(len); 205 | // FIXME: Test performance vs transmute. 206 | for _ in 0..len { 207 | buf.push(src.read_i32::()?); 208 | } 209 | Ok(buf) 210 | } 211 | 212 | #[inline] 213 | pub fn read_bare_long_array(src: &mut R) -> Result> 214 | where 215 | R: io::Read, 216 | { 217 | let len = src.read_i32::()? as usize; 218 | let mut buf = Vec::with_capacity(len); 219 | for _ in 0..len { 220 | buf.push(src.read_i64::()?); 221 | } 222 | Ok(buf) 223 | } 224 | 225 | #[inline] 226 | pub fn read_bare_string(src: &mut R) -> Result 227 | where 228 | R: io::Read, 229 | { 230 | let len = src.read_u16::()? as usize; 231 | 232 | if len == 0 { 233 | return Ok("".to_string()); 234 | } 235 | 236 | let mut bytes = vec![0; len]; 237 | let mut n_read = 0usize; 238 | while n_read < bytes.len() { 239 | match src.read(&mut bytes[n_read..])? { 240 | 0 => return Err(Error::IncompleteNbtValue), 241 | n => n_read += n, 242 | } 243 | } 244 | 245 | let decoded = from_java_cesu8(&bytes)?; 246 | Ok(decoded.into_owned()) 247 | } 248 | -------------------------------------------------------------------------------- /src/macros.rs: -------------------------------------------------------------------------------- 1 | //! Helper macros for implementing NBT format encoding and decoding via `serde`. 2 | 3 | macro_rules! return_expr_for_serialized_types_method { 4 | ($expr:expr, $func:ident($($arg:ty),*)) => { 5 | #[inline] 6 | fn $func(self, $(_: $arg,)*) 7 | -> ::std::result::Result 8 | { 9 | $expr 10 | } 11 | }; 12 | ($expr:expr, $func:ident($($arg:ty),*), result: $result:path) => { 13 | #[inline] 14 | fn $func(self, $(_: $arg,)*) 15 | -> ::std::result::Result<$result, Self::Error> 16 | { 17 | $expr 18 | } 19 | }; 20 | ($expr:expr, $func:ident($($arg:ty),*), where: $where:path) => { 21 | #[inline] 22 | fn $func<__T: ?Sized>(self, $(_: $arg,)*) 23 | -> ::std::result::Result 24 | where __T: $where 25 | { 26 | $expr 27 | } 28 | }; 29 | } 30 | 31 | macro_rules! return_expr_for_serialized_types_helper { 32 | ($expr:expr, bool) => { 33 | return_expr_for_serialized_types_method! {$expr, serialize_bool(bool)} 34 | }; 35 | ($expr:expr, i8) => { 36 | return_expr_for_serialized_types_method! {$expr, serialize_i8(i8)} 37 | }; 38 | ($expr:expr, i16) => { 39 | return_expr_for_serialized_types_method! {$expr, serialize_i16(i16)} 40 | }; 41 | ($expr:expr, i32) => { 42 | return_expr_for_serialized_types_method! {$expr, serialize_i32(i32)} 43 | }; 44 | ($expr:expr, i64) => { 45 | return_expr_for_serialized_types_method! {$expr, serialize_i64(i64)} 46 | }; 47 | ($expr:expr, u8) => { 48 | return_expr_for_serialized_types_method! {$expr, serialize_u8(u8)} 49 | }; 50 | ($expr:expr, u16) => { 51 | return_expr_for_serialized_types_method! {$expr, serialize_u16(u16)} 52 | }; 53 | ($expr:expr, u32) => { 54 | return_expr_for_serialized_types_method! {$expr, serialize_u32(u32)} 55 | }; 56 | ($expr:expr, u64) => { 57 | return_expr_for_serialized_types_method! {$expr, serialize_u64(u64)} 58 | }; 59 | ($expr:expr, f32) => { 60 | return_expr_for_serialized_types_method! {$expr, serialize_f32(f32)} 61 | }; 62 | ($expr:expr, f64) => { 63 | return_expr_for_serialized_types_method! {$expr, serialize_f64(f64)} 64 | }; 65 | ($expr:expr, char) => { 66 | return_expr_for_serialized_types_method! {$expr, serialize_char(char)} 67 | }; 68 | ($expr:expr, str) => { 69 | return_expr_for_serialized_types_method! {$expr, serialize_str(&str)} 70 | }; 71 | ($expr:expr, bytes) => { 72 | return_expr_for_serialized_types_method! {$expr, serialize_bytes(&[u8])} 73 | }; 74 | ($expr:expr, none) => { 75 | return_expr_for_serialized_types_method! {$expr, serialize_none()} 76 | }; 77 | ($expr:expr, unit) => { 78 | return_expr_for_serialized_types_method! {$expr, serialize_unit()} 79 | }; 80 | ($expr:expr, unit_struct) => { 81 | return_expr_for_serialized_types_method! { 82 | $expr, serialize_unit_struct(&'static str) 83 | } 84 | }; 85 | ($expr:expr, unit_variant) => { 86 | return_expr_for_serialized_types_method! { 87 | $expr, serialize_unit_variant(&'static str, u32, &'static str) 88 | } 89 | }; 90 | ($expr:expr, some) => { 91 | return_expr_for_serialized_types_method! { 92 | $expr, serialize_some(&__T), 93 | where: ::serde::ser::Serialize 94 | } 95 | }; 96 | ($expr:expr, newtype_struct) => { 97 | return_expr_for_serialized_types_method! { 98 | $expr, serialize_newtype_struct(&'static str, &__T), 99 | where: ::serde::ser::Serialize 100 | } 101 | }; 102 | ($expr:expr, newtype_variant) => { 103 | return_expr_for_serialized_types_method! { 104 | $expr, serialize_newtype_variant(&'static str, u32, 105 | &'static str, &__T), 106 | where: ::serde::ser::Serialize 107 | } 108 | }; 109 | ($expr:expr, seq) => { 110 | return_expr_for_serialized_types_method! { 111 | $expr, serialize_seq(Option), 112 | result: Self::SerializeSeq 113 | } 114 | }; 115 | ($expr:expr, tuple) => { 116 | return_expr_for_serialized_types_method! { 117 | $expr, serialize_tuple(usize), 118 | result: Self::SerializeTuple 119 | } 120 | }; 121 | ($expr:expr, tuple_struct) => { 122 | return_expr_for_serialized_types_method! { 123 | $expr, serialize_tuple_struct(&'static str, usize), 124 | result: Self::SerializeTupleStruct 125 | } 126 | }; 127 | ($expr:expr, tuple_variant) => { 128 | return_expr_for_serialized_types_method! { 129 | $expr, serialize_tuple_variant(&'static str, u32, &'static str, 130 | usize), 131 | result: Self::SerializeTupleVariant 132 | } 133 | }; 134 | ($expr:expr, map) => { 135 | return_expr_for_serialized_types_method! { 136 | $expr, serialize_map(Option), 137 | result: Self::SerializeMap 138 | } 139 | }; 140 | ($expr:expr, struct) => { 141 | return_expr_for_serialized_types_method! { 142 | $expr, serialize_struct(&'static str, usize), 143 | result: Self::SerializeStruct 144 | } 145 | }; 146 | ($expr:expr, struct_variant) => { 147 | return_expr_for_serialized_types_method! { 148 | $expr, serialize_struct_variant(&'static str, u32, &'static str, 149 | usize), 150 | result: Self::SerializeStructVariant 151 | } 152 | }; 153 | } 154 | 155 | /// Helper macro for implementing the `serde::se::Serializer` trait. 156 | /// 157 | /// Implement the serializer methods for each `$type` with body `$expr`. 158 | /// 159 | /// This macro is very similar to `serde::forward_to_deserialize`, but instead 160 | /// of "forwarding" it allows arbitrary expressions in the body, so long as they 161 | /// are all the same. 162 | /// 163 | /// This macro can be used to stub out `Serializer` implementations. 164 | macro_rules! return_expr_for_serialized_types { 165 | ($expr:expr; $($type:tt)*) => { 166 | $(return_expr_for_serialized_types_helper!{$expr, $type})* 167 | }; 168 | } 169 | 170 | macro_rules! unrepresentable { 171 | ($($type:tt)*) => { 172 | $(return_expr_for_serialized_types_helper!{Err(Error::UnrepresentableType(stringify!($type))), $type})* 173 | }; 174 | } 175 | 176 | /// Serde `serialize_with` implementation for array serialization. 177 | /// 178 | /// This macro provides the function body for `i8_array`, `i32_array` and `i64_array` 179 | /// in [`self::ser`], providing NBT `ByteArray`, `IntArray` and `LongArray` 180 | /// serialization with serde. 181 | macro_rules! array_serializer { 182 | ($func_name:literal, $arr: ident, $serializer: ident) => {{ 183 | use serde::ser::SerializeTupleStruct; 184 | use std::borrow::Borrow; 185 | 186 | let error = concat!( 187 | $func_name, 188 | " serializer may only be used with known-length collections" 189 | ); 190 | let magic = concat!("__hematite_nbt_", $func_name, "__"); 191 | 192 | let mut iter = $arr.into_iter(); 193 | let (length, max_length) = iter.size_hint(); 194 | 195 | if max_length.is_none() || length != max_length.unwrap() { 196 | return Err(SerError::custom(error)); 197 | } 198 | 199 | let mut seq = $serializer.serialize_tuple_struct(magic, length)?; 200 | for _i in 0..length { 201 | seq.serialize_field(iter.next().ok_or_else(|| SerError::custom(error))?.borrow())?; 202 | } 203 | 204 | if iter.next().is_some() { 205 | Err(SerError::custom(error)) 206 | } else { 207 | seq.end() 208 | } 209 | }}; 210 | } 211 | -------------------------------------------------------------------------------- /src/blob.rs: -------------------------------------------------------------------------------- 1 | use crate::Map; 2 | use std::fmt; 3 | use std::io; 4 | use std::ops::Index; 5 | 6 | use byteorder::WriteBytesExt; 7 | use flate2::read::{GzDecoder, ZlibDecoder}; 8 | use flate2::write::{GzEncoder, ZlibEncoder}; 9 | use flate2::Compression; 10 | 11 | use error::{Error, Result}; 12 | use raw; 13 | use value::Value; 14 | 15 | /// A generic, complete object in Named Binary Tag format. 16 | /// 17 | /// This is essentially a map of names to `Value`s, with an optional top-level 18 | /// name of its own. It can be created in a similar way to a `HashMap`, or read 19 | /// from an `io::Read` source, and its binary representation can be written to 20 | /// an `io::Write` destination. 21 | /// 22 | /// These read and write methods support both uncompressed and compressed 23 | /// (through Gzip or zlib compression) methods. 24 | /// 25 | /// ```rust 26 | /// use nbt::{Blob, Value}; 27 | /// 28 | /// // Create a `Blob` from key/value pairs. 29 | /// let mut nbt = Blob::new(); 30 | /// nbt.insert("name", "Herobrine").unwrap(); 31 | /// nbt.insert("health", 100i8).unwrap(); 32 | /// nbt.insert("food", 20.0f32).unwrap(); 33 | /// 34 | /// // Write a compressed binary representation to a byte array. 35 | /// let mut dst = Vec::new(); 36 | /// nbt.to_zlib_writer(&mut dst).unwrap(); 37 | /// ``` 38 | #[derive(Clone, Debug, Default, PartialEq)] 39 | pub struct Blob { 40 | title: String, 41 | content: Map, 42 | } 43 | 44 | impl Blob { 45 | /// Create a new NBT file format representation with an empty name. 46 | pub fn new() -> Blob { 47 | Blob { 48 | title: "".to_string(), 49 | content: Map::new(), 50 | } 51 | } 52 | 53 | /// Create a new NBT file format representation with the given name. 54 | pub fn named(name: S) -> Blob 55 | where 56 | S: Into, 57 | { 58 | Blob { 59 | title: name.into(), 60 | content: Map::new(), 61 | } 62 | } 63 | 64 | /// Extracts an `Blob` object from an `io::Read` source. 65 | pub fn from_reader(src: &mut R) -> Result 66 | where 67 | R: io::Read, 68 | { 69 | let (tag, title) = raw::emit_next_header(src)?; 70 | // Although it would be possible to read NBT format files composed of 71 | // arbitrary objects using the current API, by convention all files 72 | // have a top-level Compound. 73 | if tag != 0x0a { 74 | return Err(Error::NoRootCompound); 75 | } 76 | let content = Value::from_reader(tag, src)?; 77 | match content { 78 | Value::Compound(map) => Ok(Blob { 79 | title, 80 | content: map, 81 | }), 82 | _ => Err(Error::NoRootCompound), 83 | } 84 | } 85 | 86 | /// Extracts an `Blob` object from an `io::Read` source that is 87 | /// compressed using the Gzip format. 88 | pub fn from_gzip_reader(src: &mut R) -> Result 89 | where 90 | R: io::Read, 91 | { 92 | // Reads the gzip header, and fails if it is incorrect. 93 | let mut data = GzDecoder::new(src); 94 | Blob::from_reader(&mut data) 95 | } 96 | 97 | /// Extracts an `Blob` object from an `io::Read` source that is 98 | /// compressed using the zlib format. 99 | pub fn from_zlib_reader(src: &mut R) -> Result 100 | where 101 | R: io::Read, 102 | { 103 | Blob::from_reader(&mut ZlibDecoder::new(src)) 104 | } 105 | 106 | /// Writes the binary representation of this `Blob` to an `io::Write` 107 | /// destination. 108 | pub fn to_writer(&self, mut dst: &mut W) -> Result<()> 109 | where 110 | W: io::Write, 111 | { 112 | dst.write_u8(0x0a)?; 113 | raw::write_bare_string(&mut dst, &self.title)?; 114 | for (name, ref nbt) in self.content.iter() { 115 | dst.write_u8(nbt.id())?; 116 | raw::write_bare_string(&mut dst, name)?; 117 | nbt.to_writer(&mut dst)?; 118 | } 119 | raw::close_nbt(&mut dst) 120 | } 121 | 122 | /// Writes the binary representation of this `Blob`, compressed using 123 | /// the Gzip format, to an `io::Write` destination. 124 | pub fn to_gzip_writer(&self, dst: &mut W) -> Result<()> 125 | where 126 | W: io::Write, 127 | { 128 | self.to_writer(&mut GzEncoder::new(dst, Compression::default())) 129 | } 130 | 131 | /// Writes the binary representation of this `Blob`, compressed using 132 | /// the Zlib format, to an `io::Write` dst. 133 | pub fn to_zlib_writer(&self, dst: &mut W) -> Result<()> 134 | where 135 | W: io::Write, 136 | { 137 | self.to_writer(&mut ZlibEncoder::new(dst, Compression::default())) 138 | } 139 | 140 | /// Insert an `Value` with a given name into this `Blob` object. This 141 | /// method is just a thin wrapper around the underlying map method of 142 | /// the same name. 143 | /// 144 | /// This method will also return an error if a `Value::List` with 145 | /// heterogeneous elements is passed in, because this is illegal in the NBT 146 | /// file format. 147 | pub fn insert(&mut self, name: S, value: V) -> Result<()> 148 | where 149 | S: Into, 150 | V: Into, 151 | { 152 | // The follow prevents `List`s with heterogeneous tags from being 153 | // inserted into the file. 154 | let nvalue = value.into(); 155 | if let Value::List(ref vals) = nvalue { 156 | if !vals.is_empty() { 157 | let first_id = vals[0].id(); 158 | for nbt in vals { 159 | if nbt.id() != first_id { 160 | return Err(Error::HeterogeneousList); 161 | } 162 | } 163 | } 164 | } 165 | self.content.insert(name.into(), nvalue); 166 | Ok(()) 167 | } 168 | 169 | /// Tries to get a named `Value` in the blob. 170 | pub fn get(&self, name: S) -> Option<&Value> 171 | where 172 | S: Into<&'static str>, 173 | { 174 | self.content.get(name.into()) 175 | } 176 | 177 | /// The number of bytes this blob will serialize to, before compression 178 | pub fn len_bytes(&self) -> usize { 179 | /* compound tag + name length + TAG_End = 4 */ 180 | 4 + self.title.len() 181 | + self 182 | .content 183 | .iter() 184 | .map(Value::size_of_compound_entry) 185 | .sum::() 186 | } 187 | } 188 | 189 | impl<'a> Index<&'a str> for Blob { 190 | type Output = Value; 191 | 192 | fn index<'b>(&'b self, s: &'a str) -> &'b Value { 193 | self.content.get(s).unwrap() 194 | } 195 | } 196 | 197 | impl fmt::Display for Blob { 198 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 199 | write!( 200 | f, 201 | "TAG_Compound(\"{}\"): {} entry(ies)\n{{\n", 202 | self.title, 203 | self.content.len() 204 | )?; 205 | for (name, tag) in self.content.iter() { 206 | write!(f, " {}(\"{}\"): ", tag.tag_name(), name)?; 207 | tag.print(f, 2)?; 208 | writeln!(f)?; 209 | } 210 | write!(f, "}}") 211 | } 212 | } 213 | 214 | #[cfg(feature = "serde")] 215 | use serde::{self, ser::SerializeMap}; 216 | 217 | #[cfg(feature = "serde")] 218 | impl serde::Serialize for Blob { 219 | fn serialize(&self, serializer: S) -> std::result::Result 220 | where 221 | S: serde::ser::Serializer, 222 | { 223 | // No support for named Blobs. 224 | let mut state = serializer.serialize_map(Some(self.content.len()))?; 225 | for (k, v) in &self.content { 226 | state.serialize_entry(&k, &v)?; 227 | } 228 | state.end() 229 | } 230 | } 231 | 232 | #[cfg(feature = "serde")] 233 | impl<'de> serde::Deserialize<'de> for Blob { 234 | fn deserialize(deserializer: D) -> std::result::Result 235 | where 236 | D: serde::de::Deserializer<'de>, 237 | { 238 | // No support for named Blobs. 239 | let map: Map = serde::de::Deserialize::deserialize(deserializer)?; 240 | Ok(Blob { 241 | title: "".to_string(), 242 | content: map, 243 | }) 244 | } 245 | } 246 | -------------------------------------------------------------------------------- /tests/filetests.rs: -------------------------------------------------------------------------------- 1 | //! Crate for testing whether the deserialize codegen is capable of handling the 2 | //! sample NBT files in the test/ directory, which include real 3 | //! Minecraft-generated files. 4 | 5 | #[macro_use] 6 | extern crate serde_derive; 7 | extern crate serde; 8 | 9 | extern crate nbt; 10 | 11 | use std::fs::File; 12 | 13 | use nbt::de::{from_gzip_reader, from_reader}; 14 | 15 | // Include structure definitions. 16 | include!("data.rs.in"); 17 | 18 | #[test] 19 | fn deserialize_small1() { 20 | let nbt = Small1 { 21 | name: "Bananrama".to_string(), 22 | }; 23 | let mut file = File::open("tests/small1.nbt").unwrap(); 24 | let read: Small1 = from_reader(&mut file).unwrap(); 25 | assert_eq!(nbt, read) 26 | } 27 | 28 | #[test] 29 | fn deserialize_small2() { 30 | let nbt = Small2 { 31 | aaa: Small2Sub { 32 | one: 17, 33 | two: 4386, 34 | three: 287454020, 35 | }, 36 | bbb: Small2Sub { 37 | one: 17, 38 | two: 4386, 39 | three: 287454020, 40 | }, 41 | }; 42 | let mut file = File::open("tests/small2.nbt").unwrap(); 43 | let read: Small2 = from_reader(&mut file).unwrap(); 44 | assert_eq!(nbt, read) 45 | } 46 | 47 | #[test] 48 | fn deserialize_small3() { 49 | let nbt = Small3 { 50 | bbb: vec![ 51 | Small3Sub { 52 | ccc: 287454020, 53 | name: "wololo".to_string(), 54 | }, 55 | Small3Sub { 56 | ccc: 287454020, 57 | name: "wololo".to_string(), 58 | }, 59 | ], 60 | }; 61 | let mut file = File::open("tests/small3.nbt").unwrap(); 62 | let read: Small3 = from_reader(&mut file).unwrap(); 63 | assert_eq!(nbt, read) 64 | } 65 | 66 | #[test] 67 | fn deserialize_small4() { 68 | let nbt = Small4 { 69 | c1: Small4Sub { 70 | aaa: 17, 71 | bbb: 34, 72 | ccc: 51, 73 | ddd: 68, 74 | }, 75 | c2: Small4Sub { 76 | aaa: 17, 77 | bbb: 34, 78 | ccc: 51, 79 | ddd: 68, 80 | }, 81 | }; 82 | let mut file = File::open("tests/small4.nbt").unwrap(); 83 | let read: Small4 = from_reader(&mut file).unwrap(); 84 | assert_eq!(nbt, read) 85 | } 86 | 87 | #[test] 88 | fn deserialize_big1() { 89 | let nbt = Big1 { 90 | list_test_compound: vec![ 91 | Big1Sub1 { 92 | name: "Compound tag #0".to_string(), 93 | created_on: 1264099775885, 94 | }, 95 | Big1Sub1 { 96 | name: "Compound tag #1".to_string(), 97 | created_on: 1264099775885, 98 | }, 99 | ], 100 | long_test: 9223372036854775807, 101 | short_test: 32767, 102 | byte_test: 127, 103 | float_test: 0.4982314705848694, 104 | nested_compound_test: Big1Sub3 { 105 | ham: Big1Sub2 { 106 | name: "Hampus".to_string(), 107 | value: 0.75, 108 | }, 109 | egg: Big1Sub2 { 110 | name: "Eggbert".to_string(), 111 | value: 0.5, 112 | }, 113 | }, 114 | // Thanks, Notch... 115 | byte_array_test: vec![ 116 | 0, 62, 34, 16, 8, 10, 22, 44, 76, 18, 70, 32, 4, 86, 78, 80, 92, 14, 46, 88, 40, 2, 74, 117 | 56, 48, 50, 62, 84, 16, 58, 10, 72, 44, 26, 18, 20, 32, 54, 86, 28, 80, 42, 14, 96, 88, 118 | 90, 2, 24, 56, 98, 50, 12, 84, 66, 58, 60, 72, 94, 26, 68, 20, 82, 54, 36, 28, 30, 42, 119 | 64, 96, 38, 90, 52, 24, 6, 98, 0, 12, 34, 66, 8, 60, 22, 94, 76, 68, 70, 82, 4, 36, 78, 120 | 30, 92, 64, 46, 38, 40, 52, 74, 6, 48, 0, 62, 34, 16, 8, 10, 22, 44, 76, 18, 70, 32, 4, 121 | 86, 78, 80, 92, 14, 46, 88, 40, 2, 74, 56, 48, 50, 62, 84, 16, 58, 10, 72, 44, 26, 18, 122 | 20, 32, 54, 86, 28, 80, 42, 14, 96, 88, 90, 2, 24, 56, 98, 50, 12, 84, 66, 58, 60, 72, 123 | 94, 26, 68, 20, 82, 54, 36, 28, 30, 42, 64, 96, 38, 90, 52, 24, 6, 98, 0, 12, 34, 66, 124 | 8, 60, 22, 94, 76, 68, 70, 82, 4, 36, 78, 30, 92, 64, 46, 38, 40, 52, 74, 6, 48, 0, 62, 125 | 34, 16, 8, 10, 22, 44, 76, 18, 70, 32, 4, 86, 78, 80, 92, 14, 46, 88, 40, 2, 74, 56, 126 | 48, 50, 62, 84, 16, 58, 10, 72, 44, 26, 18, 20, 32, 54, 86, 28, 80, 42, 14, 96, 88, 90, 127 | 2, 24, 56, 98, 50, 12, 84, 66, 58, 60, 72, 94, 26, 68, 20, 82, 54, 36, 28, 30, 42, 64, 128 | 96, 38, 90, 52, 24, 6, 98, 0, 12, 34, 66, 8, 60, 22, 94, 76, 68, 70, 82, 4, 36, 78, 30, 129 | 92, 64, 46, 38, 40, 52, 74, 6, 48, 0, 62, 34, 16, 8, 10, 22, 44, 76, 18, 70, 32, 4, 86, 130 | 78, 80, 92, 14, 46, 88, 40, 2, 74, 56, 48, 50, 62, 84, 16, 58, 10, 72, 44, 26, 18, 20, 131 | 32, 54, 86, 28, 80, 42, 14, 96, 88, 90, 2, 24, 56, 98, 50, 12, 84, 66, 58, 60, 72, 94, 132 | 26, 68, 20, 82, 54, 36, 28, 30, 42, 64, 96, 38, 90, 52, 24, 6, 98, 0, 12, 34, 66, 8, 133 | 60, 22, 94, 76, 68, 70, 82, 4, 36, 78, 30, 92, 64, 46, 38, 40, 52, 74, 6, 48, 0, 62, 134 | 34, 16, 8, 10, 22, 44, 76, 18, 70, 32, 4, 86, 78, 80, 92, 14, 46, 88, 40, 2, 74, 56, 135 | 48, 50, 62, 84, 16, 58, 10, 72, 44, 26, 18, 20, 32, 54, 86, 28, 80, 42, 14, 96, 88, 90, 136 | 2, 24, 56, 98, 50, 12, 84, 66, 58, 60, 72, 94, 26, 68, 20, 82, 54, 36, 28, 30, 42, 64, 137 | 96, 38, 90, 52, 24, 6, 98, 0, 12, 34, 66, 8, 60, 22, 94, 76, 68, 70, 82, 4, 36, 78, 30, 138 | 92, 64, 46, 38, 40, 52, 74, 6, 48, 0, 62, 34, 16, 8, 10, 22, 44, 76, 18, 70, 32, 4, 86, 139 | 78, 80, 92, 14, 46, 88, 40, 2, 74, 56, 48, 50, 62, 84, 16, 58, 10, 72, 44, 26, 18, 20, 140 | 32, 54, 86, 28, 80, 42, 14, 96, 88, 90, 2, 24, 56, 98, 50, 12, 84, 66, 58, 60, 72, 94, 141 | 26, 68, 20, 82, 54, 36, 28, 30, 42, 64, 96, 38, 90, 52, 24, 6, 98, 0, 12, 34, 66, 8, 142 | 60, 22, 94, 76, 68, 70, 82, 4, 36, 78, 30, 92, 64, 46, 38, 40, 52, 74, 6, 48, 0, 62, 143 | 34, 16, 8, 10, 22, 44, 76, 18, 70, 32, 4, 86, 78, 80, 92, 14, 46, 88, 40, 2, 74, 56, 144 | 48, 50, 62, 84, 16, 58, 10, 72, 44, 26, 18, 20, 32, 54, 86, 28, 80, 42, 14, 96, 88, 90, 145 | 2, 24, 56, 98, 50, 12, 84, 66, 58, 60, 72, 94, 26, 68, 20, 82, 54, 36, 28, 30, 42, 64, 146 | 96, 38, 90, 52, 24, 6, 98, 0, 12, 34, 66, 8, 60, 22, 94, 76, 68, 70, 82, 4, 36, 78, 30, 147 | 92, 64, 46, 38, 40, 52, 74, 6, 48, 0, 62, 34, 16, 8, 10, 22, 44, 76, 18, 70, 32, 4, 86, 148 | 78, 80, 92, 14, 46, 88, 40, 2, 74, 56, 48, 50, 62, 84, 16, 58, 10, 72, 44, 26, 18, 20, 149 | 32, 54, 86, 28, 80, 42, 14, 96, 88, 90, 2, 24, 56, 98, 50, 12, 84, 66, 58, 60, 72, 94, 150 | 26, 68, 20, 82, 54, 36, 28, 30, 42, 64, 96, 38, 90, 52, 24, 6, 98, 0, 12, 34, 66, 8, 151 | 60, 22, 94, 76, 68, 70, 82, 4, 36, 78, 30, 92, 64, 46, 38, 40, 52, 74, 6, 48, 0, 62, 152 | 34, 16, 8, 10, 22, 44, 76, 18, 70, 32, 4, 86, 78, 80, 92, 14, 46, 88, 40, 2, 74, 56, 153 | 48, 50, 62, 84, 16, 58, 10, 72, 44, 26, 18, 20, 32, 54, 86, 28, 80, 42, 14, 96, 88, 90, 154 | 2, 24, 56, 98, 50, 12, 84, 66, 58, 60, 72, 94, 26, 68, 20, 82, 54, 36, 28, 30, 42, 64, 155 | 96, 38, 90, 52, 24, 6, 98, 0, 12, 34, 66, 8, 60, 22, 94, 76, 68, 70, 82, 4, 36, 78, 30, 156 | 92, 64, 46, 38, 40, 52, 74, 6, 48, 0, 62, 34, 16, 8, 10, 22, 44, 76, 18, 70, 32, 4, 86, 157 | 78, 80, 92, 14, 46, 88, 40, 2, 74, 56, 48, 50, 62, 84, 16, 58, 10, 72, 44, 26, 18, 20, 158 | 32, 54, 86, 28, 80, 42, 14, 96, 88, 90, 2, 24, 56, 98, 50, 12, 84, 66, 58, 60, 72, 94, 159 | 26, 68, 20, 82, 54, 36, 28, 30, 42, 64, 96, 38, 90, 52, 24, 6, 98, 0, 12, 34, 66, 8, 160 | 60, 22, 94, 76, 68, 70, 82, 4, 36, 78, 30, 92, 64, 46, 38, 40, 52, 74, 6, 48, 161 | ], 162 | string_test: "HELLO WORLD THIS IS A TEST STRING ÅÄÖ!".to_string(), 163 | list_test_long: vec![11, 12, 13, 14, 15], 164 | double_test: 0.4931287132182315, 165 | int_test: 2147483647, 166 | }; 167 | let mut file = File::open("tests/big1.nbt").unwrap(); 168 | let read: Big1 = from_gzip_reader(&mut file).unwrap(); 169 | assert_eq!(nbt, read) 170 | } 171 | 172 | #[test] 173 | fn deserialize_simple_player() { 174 | let mut file = File::open("tests/simple_player.dat").unwrap(); 175 | let _: PlayerData = from_gzip_reader(&mut file).unwrap(); 176 | } 177 | 178 | #[test] 179 | fn deserialize_complex_player() { 180 | let mut file = File::open("tests/complex_player.dat").unwrap(); 181 | let _: PlayerData = from_gzip_reader(&mut file).unwrap(); 182 | } 183 | 184 | #[test] 185 | fn deserialize_level() { 186 | let mut file = File::open("tests/level.dat").unwrap(); 187 | let _: Level = from_gzip_reader(&mut file).unwrap(); 188 | } 189 | -------------------------------------------------------------------------------- /tests/data.rs.in: -------------------------------------------------------------------------------- 1 | // Data structure snippet. Use include!() to get these definitions. 2 | 3 | #[derive(Debug, PartialEq, Deserialize)] 4 | pub struct Small1 { 5 | name: String 6 | } 7 | 8 | #[derive(Debug, PartialEq, Deserialize)] 9 | pub struct Small2Sub { 10 | #[serde(rename = "1")] one: i8, 11 | #[serde(rename = "2")] two: i16, 12 | #[serde(rename = "3")] three: i32, 13 | } 14 | 15 | #[derive(Debug, PartialEq, Deserialize)] 16 | pub struct Small2 { 17 | aaa: Small2Sub, 18 | bbb: Small2Sub, 19 | } 20 | 21 | #[derive(Debug, PartialEq, Deserialize)] 22 | pub struct Small3Sub { 23 | ccc: i32, 24 | name: String, 25 | } 26 | 27 | #[derive(Debug, PartialEq, Deserialize)] 28 | pub struct Small3 { 29 | bbb: Vec, 30 | } 31 | 32 | #[derive(Debug, PartialEq, Deserialize)] 33 | pub struct Small4Sub { 34 | aaa: i8, 35 | bbb: i8, 36 | ccc: i8, 37 | ddd: i8, 38 | } 39 | 40 | #[derive(Debug, PartialEq, Deserialize)] 41 | pub struct Small4 { 42 | c1: Small4Sub, 43 | c2: Small4Sub, 44 | } 45 | 46 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 47 | pub struct Big1Sub1 { 48 | name: String, 49 | #[serde(rename = "created-on")] created_on: i64, 50 | } 51 | 52 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 53 | pub struct Big1Sub2 { 54 | name: String, 55 | value: f32, 56 | } 57 | 58 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 59 | pub struct Big1Sub3 { 60 | ham: Big1Sub2, 61 | egg: Big1Sub2, 62 | } 63 | 64 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 65 | pub struct Big1 { 66 | #[serde(rename = "listTest (compound)")] list_test_compound: Vec, 67 | #[serde(rename = "longTest")] long_test: i64, 68 | #[serde(rename = "shortTest")] short_test: i32, 69 | #[serde(rename = "byteTest")] byte_test: i8, 70 | #[serde(rename = "floatTest")] float_test: f64, 71 | #[serde(rename = "nested compound test")] nested_compound_test: Big1Sub3, 72 | #[serde(rename = "byteArrayTest (the first 1000 values of (n*n*255+n*7)%100, starting with n=0 (0, 62, 34, 16, 8, ...))")] 73 | byte_array_test: Vec, // [i8; 1000] does not implement PartialEq. 74 | #[serde(rename = "stringTest")] string_test: String, 75 | #[serde(rename = "listTest (long)")] 76 | list_test_long: Vec, 77 | #[serde(rename = "doubleTest")] double_test: f64, 78 | #[serde(rename = "intTest")] int_test: i32, 79 | } 80 | 81 | #[derive(Debug, Serialize, Deserialize)] 82 | pub struct Level { 83 | #[serde(rename = "Data")] 84 | pub data: LevelData 85 | } 86 | 87 | #[derive(Debug, Serialize, Deserialize)] 88 | pub struct LevelData { 89 | #[serde(rename = "RandomSeed")] seed: i64, 90 | #[serde(rename = "DayTime")] daytime: i64, 91 | #[serde(rename = "Player")] player: PlayerData, 92 | initialized: bool, 93 | version: i32, 94 | #[serde(rename = "allowCommands")] allow_commands: bool, 95 | #[serde(rename = "LastPlayed")] last_played: i64, 96 | #[serde(rename = "SpawnZ")] spawn_z: i32, 97 | #[serde(rename = "SpawnX")] spawn_x: i32, 98 | #[serde(rename = "SpawnY")] spawn_y: i32, 99 | #[serde(rename = "LevelName")] name: String, 100 | #[serde(rename = "MapFeatures")] map_features: bool, 101 | 102 | #[serde(rename = "GameType")] game_type: i32, 103 | #[serde(rename = "Difficulty")] difficulty: i8, 104 | #[serde(rename = "DifficultyLocked")] difficulty_locked: bool, 105 | 106 | #[serde(rename = "generatorName")] generator_name: String, 107 | #[serde(rename = "generatorOptions")] generator_options: String, 108 | #[serde(rename = "generatorVersion")] generator_version: i32, 109 | 110 | #[serde(rename = "Time")] time: i64, 111 | #[serde(rename = "clearWeatherTime")] clear_weather_time: i32, 112 | #[serde(rename = "thunderTime")] thunder_time: i32, 113 | #[serde(rename = "rainTime")] rain_time: i32, 114 | 115 | thundering: bool, 116 | raining: bool, 117 | hardcore: bool, 118 | 119 | #[serde(rename = "GameRules")] game_rules: GameRules, 120 | #[serde(rename = "SizeOnDisk")] size_on_disk: i64, 121 | 122 | #[serde(rename = "BorderCenterX")] border_center_x: f64, 123 | #[serde(rename = "BorderCenterY")] border_center_y: Option, 124 | #[serde(rename = "BorderCenterZ")] border_center_z: f64, 125 | #[serde(rename = "BorderWarningBlocks")] border_warning_blocks: f64, 126 | #[serde(rename = "BorderWarningTime")] border_warning_time: f64, 127 | #[serde(rename = "BorderSafeZone")] border_safe_zone: f64, 128 | #[serde(rename = "BorderSize")] border_size: f64, 129 | #[serde(rename = "BorderSizeLerpTarget")] border_size_lerp_target: f64, 130 | #[serde(rename = "BorderSizeLerpTime")] border_size_lerp_time: i64, 131 | #[serde(rename = "BorderDamagePerBlock")] border_damage_per_block: f64, 132 | } 133 | 134 | #[derive(Debug, Serialize, Deserialize)] 135 | pub struct PlayerData { 136 | #[serde(rename = "PersistentId")] persistant_id: Option, 137 | #[serde(rename = "playerGameType")] game_type: i32, 138 | abilities: PlayerAbilityData, 139 | #[serde(rename = "Score")] score: Option, 140 | 141 | #[serde(rename = "Dimension")] dimension: i32, 142 | #[serde(rename = "OnGround")] on_ground: bool, 143 | #[serde(rename = "FallDistance")] fall_distance: f32, 144 | #[serde(rename = "Motion")] motion: Vec, // [f64; 3] 145 | #[serde(rename = "Pos")] position: Vec, // [f64; 3] 146 | #[serde(rename = "Rotation")] rotation: Vec, // [f32; 2] 147 | 148 | #[serde(rename = "SpawnX")] spawn_x: i32, 149 | #[serde(rename = "SpawnY")] spawn_y: i32, 150 | #[serde(rename = "SpawnZ")] spawn_z: i32, 151 | #[serde(rename = "SpawnForced")] spawn_forced: Option, 152 | 153 | #[serde(rename = "PortalCooldown")] portal_cooldown: Option, 154 | #[serde(rename = "Invulnerable")] invulnerable: Option, 155 | 156 | #[serde(rename = "AttackTime")] attack_time: Option, 157 | #[serde(rename = "HurtTime")] hurt_time: i16, 158 | #[serde(rename = "HurtByTimestamp")] hurt_by: Option, 159 | #[serde(rename = "DeathTime")] death_time: i16, 160 | #[serde(rename = "Sleeping")] sleeping: bool, 161 | #[serde(rename = "SleepTimer")] sleep_timer: i16, 162 | 163 | #[serde(rename = "Health")] health: i16, 164 | #[serde(rename = "HealF")] heal: Option, 165 | #[serde(rename = "foodLevel")] food_level: i32, 166 | #[serde(rename = "foodTickTimer")] food_tick_timer: i32, 167 | #[serde(rename = "foodSaturationLevel")] food_saturation_level: f32, 168 | #[serde(rename = "foodExhaustionLevel")] food_exhaustion_level: f32, 169 | 170 | #[serde(rename = "Fire")] fire: i16, 171 | #[serde(rename = "Air")] air: i16, 172 | 173 | #[serde(rename = "XpP")] xp_p: f32, 174 | #[serde(rename = "XpLevel")] xp_level: i32, 175 | #[serde(rename = "XpTotal")] xp_total: i32, 176 | #[serde(rename = "XpSeed")] xp_seed: Option, 177 | 178 | #[serde(rename = "Inventory")] inventory: Vec, 179 | #[serde(rename = "EnderItems")] ender_items: Vec, 180 | 181 | #[serde(rename = "SelectedItemSlot")] selected_item_slot: Option, 182 | #[serde(rename = "SelectedItem")] selected_item: Option, 183 | #[serde(rename = "UUIDLeast")] uuid_least: Option, 184 | #[serde(rename = "UUIDMost")] uuid_most: Option, 185 | #[serde(rename = "AbsorptionAmount")] absorbtion_amount: Option, 186 | #[serde(rename = "Attributes")] attributes: Option>, 187 | #[serde(rename = "ActiveEffects")] active_effects: Option>, 188 | } 189 | 190 | #[derive(Debug, Serialize, Deserialize)] 191 | pub struct PlayerAbilityData { 192 | invulnerable: bool, 193 | instabuild: bool, 194 | flying: bool, 195 | #[serde(rename = "flySpeed")] fly_speed: f32, 196 | #[serde(rename = "walkSpeed")] walk_speed: f32, 197 | #[serde(rename = "mayBuild")] may_build: bool, 198 | #[serde(rename = "mayfly")] may_fly: bool, 199 | } 200 | 201 | #[derive(Debug, Serialize, Deserialize)] 202 | pub struct InventoryEntry { 203 | id: String, 204 | #[serde(rename = "Slot")] slot: Option, 205 | #[serde(rename = "Count")] count: i8, 206 | #[serde(rename = "Damage")] damage: i16, 207 | #[serde(rename = "tag")] info: Option, 208 | } 209 | 210 | #[derive(Debug, Serialize, Deserialize)] 211 | pub struct InventoryEntryInfo { 212 | display: Option, 213 | #[serde(rename = "RepairCost")] repair_cost: Option, 214 | #[serde(rename = "ench")] enchantments: Vec, 215 | } 216 | 217 | #[derive(Debug, Serialize, Deserialize)] 218 | pub struct InventoryEntryDisplay { 219 | #[serde(rename = "Name")] name: String, 220 | } 221 | 222 | #[derive(Debug, Serialize, Deserialize)] 223 | pub struct Enchantment { 224 | id: i16, 225 | #[serde(rename = "lvl")] level: i16, 226 | } 227 | 228 | #[derive(Debug, Serialize, Deserialize)] 229 | pub struct EnderItemsEntry { 230 | id: String, 231 | } 232 | 233 | #[derive(Debug, Serialize, Deserialize)] 234 | pub struct AttributeEntry { 235 | #[serde(rename = "Name")] name: String, 236 | #[serde(rename = "Base")] base: f64, 237 | #[serde(rename = "Modifiers")] modifiers: Option>, 238 | } 239 | 240 | #[derive(Debug, Serialize, Deserialize)] 241 | pub struct AttributeModifier { 242 | #[serde(rename = "Name")] name: String, 243 | #[serde(rename = "Amount")] amount: f64, 244 | #[serde(rename = "Operation")] operation: i32, 245 | #[serde(rename = "UUIDLeast")] uuid_least: i64, 246 | #[serde(rename = "UUIDMost")] uuid_most: i64, 247 | } 248 | 249 | #[derive(Debug, Serialize, Deserialize)] 250 | pub struct ActiveEffect { 251 | #[serde(rename = "Id")] id: i8, 252 | #[serde(rename = "Duration")] base: i32, 253 | #[serde(rename = "Ambient")] ambient: bool, 254 | #[serde(rename = "Amplifier")] amplifier: bool, 255 | #[serde(rename = "ShowParticles")] show_particles: bool, 256 | } 257 | 258 | #[derive(Debug, Serialize, Deserialize)] 259 | pub struct GameRules { 260 | #[serde(rename = "doMobLoot")] mob_loot: String, 261 | #[serde(rename = "doTileDrops")] tile_drops: String, 262 | #[serde(rename = "doFireTick")] fire_tick: String, 263 | #[serde(rename = "mobGriefing")] mob_griefing: String, 264 | #[serde(rename = "commandBlockOutput")] command_block_output: String, 265 | #[serde(rename = "doMobSpawning")] mob_spawning: String, 266 | #[serde(rename = "keepInventory")] keep_inventory: String, 267 | #[serde(rename = "showDeathMessages")] show_death_messages: String, 268 | #[serde(rename = "doEntityDrops")] entity_drops: String, 269 | #[serde(rename = "naturalRegeneration")] natural_regeneration: String, 270 | #[serde(rename = "logAdminCommands")] log_admin_commands: String, 271 | #[serde(rename = "doDaylightCycle")] daylight_cycle: String, 272 | #[serde(rename = "sendCommandFeedback")] send_command_feedback: String, 273 | #[serde(rename = "randomTickSpeed")] random_tick_speed: String, 274 | #[serde(rename = "reducedDebugInfo")] reduced_debug_info: String, 275 | } 276 | -------------------------------------------------------------------------------- /src/de.rs: -------------------------------------------------------------------------------- 1 | //! Deserialize Named Binary Tag data to a Rust data structure. 2 | 3 | use std::io; 4 | 5 | use flate2::read; 6 | use serde::de; 7 | 8 | use raw; 9 | 10 | use error::{Error, Result}; 11 | 12 | /// Decode an object from Named Binary Tag (NBT) format. 13 | /// 14 | /// Note that only maps and structs can be decoded, because the NBT format does 15 | /// not support bare types. Other types will return `Error::NoRootCompound`. 16 | pub fn from_reader(src: R) -> Result 17 | where 18 | R: io::Read, 19 | T: de::DeserializeOwned, 20 | { 21 | let mut decoder = Decoder::new(src); 22 | de::Deserialize::deserialize(&mut decoder) 23 | } 24 | 25 | /// Decode an object from Named Binary Tag (NBT) format. 26 | /// 27 | /// Note that only maps and structs can be decoded, because the NBT format does 28 | /// not support bare types. Other types will return `Error::NoRootCompound`. 29 | pub fn from_gzip_reader(src: R) -> Result 30 | where 31 | R: io::Read, 32 | T: de::DeserializeOwned, 33 | { 34 | let gzip = read::GzDecoder::new(src); 35 | from_reader(gzip) 36 | } 37 | 38 | /// Decode an object from Named Binary Tag (NBT) format. 39 | /// 40 | /// Note that only maps and structs can be decoded, because the NBT format does 41 | /// not support bare types. Other types will return `Error::NoRootCompound`. 42 | pub fn from_zlib_reader(src: R) -> Result 43 | where 44 | R: io::Read, 45 | T: de::DeserializeOwned, 46 | { 47 | let zlib = read::ZlibDecoder::new(src); 48 | from_reader(zlib) 49 | } 50 | 51 | /// Decode objects from Named Binary Tag (NBT) format. 52 | /// 53 | /// Note that only maps and structs can be decoded, because the NBT format does 54 | /// not support bare types. Other types will return `Error::NoRootCompound`. 55 | pub struct Decoder { 56 | reader: R, 57 | } 58 | 59 | impl Decoder 60 | where 61 | R: io::Read, 62 | { 63 | /// Create an NBT Decoder from a given `io::Read` source. 64 | pub fn new(src: R) -> Self { 65 | Decoder { reader: src } 66 | } 67 | } 68 | 69 | impl<'de: 'a, 'a, R: io::Read> de::Deserializer<'de> for &'a mut Decoder { 70 | type Error = Error; 71 | 72 | fn deserialize_any(self, _visitor: V) -> Result 73 | where 74 | V: de::Visitor<'de>, 75 | { 76 | // The decoder cannot deserialize types by default. It can only handle 77 | // maps and structs. 78 | Err(Error::NoRootCompound) 79 | } 80 | 81 | fn deserialize_struct( 82 | self, 83 | _name: &'static str, 84 | _fields: &'static [&'static str], 85 | visitor: V, 86 | ) -> Result 87 | where 88 | V: de::Visitor<'de>, 89 | { 90 | self.deserialize_map(visitor) 91 | } 92 | 93 | fn deserialize_unit_struct(self, _name: &'static str, visitor: V) -> Result 94 | where 95 | V: de::Visitor<'de>, 96 | { 97 | visitor.visit_unit() 98 | } 99 | 100 | /// Deserialize newtype structs by their underlying types. 101 | fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result 102 | where 103 | V: de::Visitor<'de>, 104 | { 105 | visitor.visit_newtype_struct(self) 106 | } 107 | 108 | fn deserialize_map(self, visitor: V) -> Result 109 | where 110 | V: de::Visitor<'de>, 111 | { 112 | // Ignore the header (if there is one). 113 | let (tag, _) = raw::emit_next_header(&mut self.reader)?; 114 | 115 | match tag { 116 | 0x0a => visitor.visit_map(MapDecoder::new(self)), 117 | _ => Err(Error::NoRootCompound), 118 | } 119 | } 120 | 121 | forward_to_deserialize_any! { 122 | bool u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string bytes byte_buf 123 | unit seq tuple_struct tuple option enum identifier ignored_any 124 | } 125 | } 126 | 127 | /// Decoder for map-like types. 128 | struct MapDecoder<'a, R: io::Read + 'a> { 129 | outer: &'a mut Decoder, 130 | tag: Option, 131 | } 132 | 133 | impl<'a, R> MapDecoder<'a, R> 134 | where 135 | R: io::Read, 136 | { 137 | fn new(outer: &'a mut Decoder) -> Self { 138 | MapDecoder { outer, tag: None } 139 | } 140 | } 141 | 142 | impl<'de: 'a, 'a, R: io::Read + 'a> de::MapAccess<'de> for MapDecoder<'a, R> { 143 | type Error = Error; 144 | 145 | fn next_key_seed(&mut self, seed: K) -> Result> 146 | where 147 | K: de::DeserializeSeed<'de>, 148 | { 149 | let tag = raw::read_bare_byte(&mut self.outer.reader)?; 150 | 151 | // NBT indicates the end of a compound type with a 0x00 tag. 152 | if tag == 0x00 { 153 | return Ok(None); 154 | } 155 | 156 | // Keep track of the tag so that we can decode the field correctly. 157 | self.tag = Some(tag as u8); 158 | 159 | // TODO: Enforce that keys must be String. This is a bit of a hack. 160 | let mut de = InnerDecoder { 161 | outer: self.outer, 162 | tag: 0x08, 163 | }; 164 | 165 | Ok(Some(seed.deserialize(&mut de)?)) 166 | } 167 | 168 | fn next_value_seed(&mut self, seed: V) -> Result 169 | where 170 | V: de::DeserializeSeed<'de>, 171 | { 172 | let mut de = match self.tag { 173 | Some(tag) => InnerDecoder { 174 | outer: self.outer, 175 | tag, 176 | }, 177 | None => unimplemented!(), 178 | }; 179 | Ok(seed.deserialize(&mut de)?) 180 | } 181 | } 182 | 183 | /// Decoder for list-like types. 184 | struct SeqDecoder<'a, R: io::Read + 'a> { 185 | outer: &'a mut Decoder, 186 | tag: u8, 187 | length: i32, 188 | current: i32, 189 | } 190 | 191 | impl<'a, R> SeqDecoder<'a, R> 192 | where 193 | R: io::Read, 194 | { 195 | fn list(outer: &'a mut Decoder) -> Result { 196 | let tag = raw::read_bare_byte(&mut outer.reader)?; 197 | let length = raw::read_bare_int(&mut outer.reader)?; 198 | Ok(SeqDecoder { 199 | outer, 200 | tag: tag as u8, 201 | length, 202 | current: 0, 203 | }) 204 | } 205 | 206 | fn byte_array(outer: &'a mut Decoder) -> Result { 207 | let length = raw::read_bare_int(&mut outer.reader)?; 208 | Ok(SeqDecoder { 209 | outer, 210 | tag: 0x01, 211 | length, 212 | current: 0, 213 | }) 214 | } 215 | 216 | fn int_array(outer: &'a mut Decoder) -> Result { 217 | let length = raw::read_bare_int(&mut outer.reader)?; 218 | Ok(SeqDecoder { 219 | outer, 220 | tag: 0x03, 221 | length, 222 | current: 0, 223 | }) 224 | } 225 | 226 | fn long_array(outer: &'a mut Decoder) -> Result { 227 | let length = raw::read_bare_int(&mut outer.reader)?; 228 | Ok(SeqDecoder { 229 | outer, 230 | tag: 0x04, 231 | length, 232 | current: 0, 233 | }) 234 | } 235 | } 236 | 237 | impl<'de: 'a, 'a, R: io::Read + 'a> de::SeqAccess<'de> for SeqDecoder<'a, R> { 238 | type Error = Error; 239 | 240 | fn next_element_seed(&mut self, seed: K) -> Result> 241 | where 242 | K: de::DeserializeSeed<'de>, 243 | { 244 | if self.current == self.length { 245 | return Ok(None); 246 | } 247 | 248 | let mut de = InnerDecoder { 249 | outer: self.outer, 250 | tag: self.tag, 251 | }; 252 | let value = seed.deserialize(&mut de)?; 253 | 254 | self.current += 1; 255 | 256 | Ok(Some(value)) 257 | } 258 | 259 | /// We always know the length of an NBT list in advance. 260 | fn size_hint(&self) -> Option { 261 | Some(self.length as usize) 262 | } 263 | } 264 | 265 | /// Private inner decoder, for decoding raw (i.e. non-Compound) types. 266 | struct InnerDecoder<'a, R: io::Read + 'a> { 267 | outer: &'a mut Decoder, 268 | tag: u8, 269 | } 270 | 271 | impl<'a, 'b: 'a, 'de, R: io::Read> de::Deserializer<'de> for &'b mut InnerDecoder<'a, R> { 272 | type Error = Error; 273 | 274 | fn deserialize_any(self, visitor: V) -> Result 275 | where 276 | V: de::Visitor<'de>, 277 | { 278 | let outer = &mut self.outer; 279 | 280 | match self.tag { 281 | 0x01 => visitor.visit_i8(raw::read_bare_byte(&mut outer.reader)?), 282 | 0x02 => visitor.visit_i16(raw::read_bare_short(&mut outer.reader)?), 283 | 0x03 => visitor.visit_i32(raw::read_bare_int(&mut outer.reader)?), 284 | 0x04 => visitor.visit_i64(raw::read_bare_long(&mut outer.reader)?), 285 | 0x05 => visitor.visit_f32(raw::read_bare_float(&mut outer.reader)?), 286 | 0x06 => visitor.visit_f64(raw::read_bare_double(&mut outer.reader)?), 287 | 0x07 => visitor.visit_seq(SeqDecoder::byte_array(outer)?), 288 | 0x08 => visitor.visit_string(raw::read_bare_string(&mut outer.reader)?), 289 | 0x09 => visitor.visit_seq(SeqDecoder::list(outer)?), 290 | 0x0a => visitor.visit_map(MapDecoder::new(outer)), 291 | 0x0b => visitor.visit_seq(SeqDecoder::int_array(outer)?), 292 | 0x0c => visitor.visit_seq(SeqDecoder::long_array(outer)?), 293 | t => Err(Error::InvalidTypeId(t)), 294 | } 295 | } 296 | 297 | /// Deserialize bool values from a byte. Fail if that byte is not 0 or 1. 298 | fn deserialize_bool(self, visitor: V) -> Result 299 | where 300 | V: de::Visitor<'de>, 301 | { 302 | match self.tag { 303 | 0x01 => { 304 | let reader = &mut self.outer.reader; 305 | let value = raw::read_bare_byte(reader)?; 306 | match value { 307 | 0 => visitor.visit_bool(false), 308 | 1 => visitor.visit_bool(true), 309 | b => Err(Error::NonBooleanByte(b)), 310 | } 311 | } 312 | _ => Err(Error::TagMismatch(self.tag, 0x01)), 313 | } 314 | } 315 | 316 | /// Interpret missing values as None. 317 | fn deserialize_option(self, visitor: V) -> Result 318 | where 319 | V: de::Visitor<'de>, 320 | { 321 | visitor.visit_some(self) 322 | } 323 | 324 | fn deserialize_unit(self, visitor: V) -> Result 325 | where 326 | V: de::Visitor<'de>, 327 | { 328 | visitor.visit_unit() 329 | } 330 | 331 | fn deserialize_unit_struct(self, _name: &'static str, visitor: V) -> Result 332 | where 333 | V: de::Visitor<'de>, 334 | { 335 | visitor.visit_unit() 336 | } 337 | 338 | /// Deserialize newtype structs by their underlying types. 339 | fn deserialize_newtype_struct(self, _name: &'static str, visitor: V) -> Result 340 | where 341 | V: de::Visitor<'de>, 342 | { 343 | visitor.visit_newtype_struct(self) 344 | } 345 | 346 | forward_to_deserialize_any! { 347 | u8 u16 u32 u64 i8 i16 i32 i64 f32 f64 char str string bytes byte_buf seq 348 | map tuple_struct struct tuple enum identifier ignored_any 349 | } 350 | } 351 | -------------------------------------------------------------------------------- /src/value.rs: -------------------------------------------------------------------------------- 1 | use crate::Map; 2 | use std::fmt; 3 | use std::io; 4 | 5 | use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; 6 | 7 | use error::{Error, Result}; 8 | use raw; 9 | 10 | /// Values which can be represented in the Named Binary Tag format. 11 | #[derive(Clone, Debug, PartialEq)] 12 | #[cfg_attr(feature = "serde", derive(Serialize))] 13 | #[cfg_attr(feature = "serde", derive(Deserialize))] 14 | #[cfg_attr(feature = "serde", serde(untagged))] 15 | pub enum Value { 16 | Byte(i8), 17 | Short(i16), 18 | Int(i32), 19 | Long(i64), 20 | Float(f32), 21 | Double(f64), 22 | ByteArray(Vec), 23 | String(String), 24 | List(Vec), 25 | Compound(Map), 26 | #[cfg_attr(feature = "serde", serde(serialize_with = "crate::i32_array"))] 27 | IntArray(Vec), 28 | #[cfg_attr(feature = "serde", serde(serialize_with = "crate::i64_array"))] 29 | LongArray(Vec), 30 | } 31 | 32 | impl Value { 33 | /// The type ID of this `Value`, which is a single byte in the range 34 | /// `0x01` to `0x0b`. 35 | pub fn id(&self) -> u8 { 36 | match *self { 37 | Value::Byte(_) => 0x01, 38 | Value::Short(_) => 0x02, 39 | Value::Int(_) => 0x03, 40 | Value::Long(_) => 0x04, 41 | Value::Float(_) => 0x05, 42 | Value::Double(_) => 0x06, 43 | Value::ByteArray(_) => 0x07, 44 | Value::String(_) => 0x08, 45 | Value::List(_) => 0x09, 46 | Value::Compound(_) => 0x0a, 47 | Value::IntArray(_) => 0x0b, 48 | Value::LongArray(_) => 0x0c, 49 | } 50 | } 51 | 52 | /// A string representation of this tag. 53 | pub fn tag_name(&self) -> &str { 54 | match *self { 55 | Value::Byte(_) => "TAG_Byte", 56 | Value::Short(_) => "TAG_Short", 57 | Value::Int(_) => "TAG_Int", 58 | Value::Long(_) => "TAG_Long", 59 | Value::Float(_) => "TAG_Float", 60 | Value::Double(_) => "TAG_Double", 61 | Value::ByteArray(_) => "TAG_ByteArray", 62 | Value::String(_) => "TAG_String", 63 | Value::List(_) => "TAG_List", 64 | Value::Compound(_) => "TAG_Compound", 65 | Value::IntArray(_) => "TAG_IntArray", 66 | Value::LongArray(_) => "TAG_LongArray", 67 | } 68 | } 69 | 70 | /// Writes the payload of this `Value` to an `io::Write` destination. 71 | pub fn to_writer(&self, mut dst: &mut W) -> Result<()> 72 | where 73 | W: io::Write, 74 | { 75 | match *self { 76 | Value::Byte(val) => raw::write_bare_byte(dst, val), 77 | Value::Short(val) => raw::write_bare_short(dst, val), 78 | Value::Int(val) => raw::write_bare_int(dst, val), 79 | Value::Long(val) => raw::write_bare_long(dst, val), 80 | Value::Float(val) => raw::write_bare_float(dst, val), 81 | Value::Double(val) => raw::write_bare_double(dst, val), 82 | Value::ByteArray(ref vals) => raw::write_bare_byte_array(dst, &vals[..]), 83 | Value::String(ref val) => raw::write_bare_string(dst, &val), 84 | Value::List(ref vals) => { 85 | // This is a bit of a trick: if the list is empty, don't bother 86 | // checking its type. 87 | if vals.is_empty() { 88 | dst.write_u8(0)?; // TAG_End 89 | dst.write_i32::(0)?; 90 | } else { 91 | // Otherwise, use the first element of the list. 92 | let first_id = vals[0].id(); 93 | dst.write_u8(first_id)?; 94 | dst.write_i32::(vals.len() as i32)?; 95 | for nbt in vals { 96 | // Ensure that all of the tags are the same type. 97 | if nbt.id() != first_id { 98 | return Err(Error::HeterogeneousList); 99 | } 100 | nbt.to_writer(dst)?; 101 | } 102 | } 103 | Ok(()) 104 | } 105 | Value::Compound(ref vals) => { 106 | for (name, ref nbt) in vals { 107 | // Write the header for the tag. 108 | dst.write_u8(nbt.id())?; 109 | raw::write_bare_string(dst, name)?; 110 | nbt.to_writer(dst)?; 111 | } 112 | raw::close_nbt(&mut dst) 113 | } 114 | Value::IntArray(ref vals) => raw::write_bare_int_array(dst, &vals[..]), 115 | Value::LongArray(ref vals) => raw::write_bare_long_array(dst, &vals[..]), 116 | } 117 | } 118 | 119 | /// Reads the payload of an `Value` with a given type ID from an 120 | /// `io::Read` source. 121 | pub fn from_reader(id: u8, src: &mut R) -> Result 122 | where 123 | R: io::Read, 124 | { 125 | match id { 126 | 0x01 => Ok(Value::Byte(raw::read_bare_byte(src)?)), 127 | 0x02 => Ok(Value::Short(raw::read_bare_short(src)?)), 128 | 0x03 => Ok(Value::Int(raw::read_bare_int(src)?)), 129 | 0x04 => Ok(Value::Long(raw::read_bare_long(src)?)), 130 | 0x05 => Ok(Value::Float(raw::read_bare_float(src)?)), 131 | 0x06 => Ok(Value::Double(raw::read_bare_double(src)?)), 132 | 0x07 => Ok(Value::ByteArray(raw::read_bare_byte_array(src)?)), 133 | 0x08 => Ok(Value::String(raw::read_bare_string(src)?)), 134 | 0x09 => { 135 | // List 136 | let id = src.read_u8()?; 137 | let len = src.read_i32::()? as usize; 138 | let mut buf = Vec::with_capacity(len); 139 | for _ in 0..len { 140 | buf.push(Value::from_reader(id, src)?); 141 | } 142 | Ok(Value::List(buf)) 143 | } 144 | 0x0a => { 145 | // Compound 146 | let mut buf = Map::new(); 147 | loop { 148 | let (id, name) = raw::emit_next_header(src)?; 149 | if id == 0x00 { 150 | break; 151 | } 152 | let tag = Value::from_reader(id, src)?; 153 | buf.insert(name, tag); 154 | } 155 | Ok(Value::Compound(buf)) 156 | } 157 | 0x0b => Ok(Value::IntArray(raw::read_bare_int_array(src)?)), 158 | 0x0c => Ok(Value::LongArray(raw::read_bare_long_array(src)?)), 159 | e => Err(Error::InvalidTypeId(e)), 160 | } 161 | } 162 | 163 | pub fn print(&self, f: &mut fmt::Formatter, offset: usize) -> fmt::Result { 164 | match *self { 165 | Value::Byte(v) => write!(f, "{}", v), 166 | Value::Short(v) => write!(f, "{}", v), 167 | Value::Int(v) => write!(f, "{}", v), 168 | Value::Long(v) => write!(f, "{}", v), 169 | Value::Float(v) => write!(f, "{}", v), 170 | Value::Double(v) => write!(f, "{}", v), 171 | Value::ByteArray(ref v) => write!(f, "{:?}", v), 172 | Value::String(ref v) => write!(f, "{}", v), 173 | Value::IntArray(ref v) => write!(f, "{:?}", v), 174 | Value::LongArray(ref v) => write!(f, "{:?}", v), 175 | Value::List(ref v) => { 176 | if v.is_empty() { 177 | write!(f, "zero entries") 178 | } else { 179 | write!( 180 | f, 181 | "{} entries of type {}\n{:>width$}\n", 182 | v.len(), 183 | v[0].tag_name(), 184 | "{", 185 | width = offset + 1 186 | )?; 187 | for tag in v { 188 | let new_offset = offset + 2; 189 | write!( 190 | f, 191 | "{:>width$}(None): ", 192 | tag.tag_name(), 193 | width = new_offset + tag.tag_name().len() 194 | )?; 195 | tag.print(f, new_offset)?; 196 | writeln!(f)?; 197 | } 198 | write!(f, "{:>width$}", "}", width = offset + 1) 199 | } 200 | } 201 | Value::Compound(ref v) => { 202 | write!( 203 | f, 204 | "{} entry(ies)\n{:>width$}\n", 205 | v.len(), 206 | "{", 207 | width = offset + 1 208 | )?; 209 | for (name, tag) in v { 210 | let new_offset = offset + 2; 211 | write!( 212 | f, 213 | "{:>width$}({}): ", 214 | tag.tag_name(), 215 | name, 216 | width = new_offset + tag.tag_name().len() 217 | )?; 218 | tag.print(f, new_offset)?; 219 | writeln!(f)?; 220 | } 221 | write!(f, "{:>width$}", "}", width = offset + 1) 222 | } 223 | } 224 | } 225 | 226 | /// The number of bytes this value serializes to, before compression 227 | pub fn len_bytes(&self) -> usize { 228 | 1 /* type ID */ + self.len_payload() 229 | } 230 | 231 | /// Serialized size of an entry within a TAG_COMPOUND 232 | /// Also used by Blob, so crate visible 233 | pub(crate) fn size_of_compound_entry((key, value): (&String, &Value)) -> usize { 234 | let key_len = 2 + key.len(); 235 | let value_len = value.len_bytes(); 236 | key_len + value_len 237 | } 238 | 239 | // The serialized size of the payload specifically, without tag IDs or names prepended. 240 | fn len_payload(&self) -> usize { 241 | use std::mem::size_of; 242 | match self { 243 | Value::Byte(_) => 1, 244 | Value::Short(_) => 2, 245 | Value::Int(_) => 4, 246 | Value::Long(_) => 8, 247 | Value::Float(_) => 4, 248 | Value::Double(_) => 8, 249 | Value::String(s) => 2 /* string size */ + s.len(), 250 | Value::List(v) => { 251 | 1 /* item tag */ + 4 /* arr size */ + v.iter().map(Self::len_payload).sum::() 252 | } 253 | Value::Compound(hm) => { 254 | hm.iter().map(Self::size_of_compound_entry).sum::() + 1usize 255 | } 256 | Value::ByteArray(ba) => 4 /* arr size */ + size_of::()*ba.len(), 257 | Value::IntArray(ia) => 4 /* arr size */ + size_of::()*ia.len(), 258 | Value::LongArray(la) => 4 /* arr size */ + size_of::()*la.len(), 259 | } 260 | } 261 | } 262 | 263 | impl fmt::Display for Value { 264 | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { 265 | self.print(f, 0) 266 | } 267 | } 268 | 269 | impl From for Value { 270 | fn from(t: i8) -> Value { 271 | Value::Byte(t) 272 | } 273 | } 274 | 275 | impl From for Value { 276 | fn from(t: i16) -> Value { 277 | Value::Short(t) 278 | } 279 | } 280 | 281 | impl From for Value { 282 | fn from(t: i32) -> Value { 283 | Value::Int(t) 284 | } 285 | } 286 | 287 | impl From for Value { 288 | fn from(t: i64) -> Value { 289 | Value::Long(t) 290 | } 291 | } 292 | 293 | impl From for Value { 294 | fn from(t: f32) -> Value { 295 | Value::Float(t) 296 | } 297 | } 298 | 299 | impl From for Value { 300 | fn from(t: f64) -> Value { 301 | Value::Double(t) 302 | } 303 | } 304 | 305 | impl<'a> From<&'a str> for Value { 306 | fn from(t: &'a str) -> Value { 307 | Value::String(t.into()) 308 | } 309 | } 310 | 311 | impl From for Value { 312 | fn from(t: String) -> Value { 313 | Value::String(t) 314 | } 315 | } 316 | 317 | impl From> for Value { 318 | fn from(t: Vec) -> Value { 319 | Value::ByteArray(t) 320 | } 321 | } 322 | 323 | impl<'a> From<&'a [i8]> for Value { 324 | fn from(t: &'a [i8]) -> Value { 325 | Value::ByteArray(t.into()) 326 | } 327 | } 328 | 329 | impl From> for Value { 330 | fn from(t: Vec) -> Value { 331 | Value::IntArray(t) 332 | } 333 | } 334 | 335 | impl<'a> From<&'a [i32]> for Value { 336 | fn from(t: &'a [i32]) -> Value { 337 | Value::IntArray(t.into()) 338 | } 339 | } 340 | 341 | impl From> for Value { 342 | fn from(t: Vec) -> Value { 343 | Value::LongArray(t) 344 | } 345 | } 346 | 347 | impl<'a> From<&'a [i64]> for Value { 348 | fn from(t: &'a [i64]) -> Value { 349 | Value::LongArray(t.into()) 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /src/tests.rs: -------------------------------------------------------------------------------- 1 | use std::fs::File; 2 | use std::io; 3 | use Map; 4 | 5 | //use test::Bencher; 6 | 7 | use blob::Blob; 8 | use error::Error; 9 | use value::Value; 10 | 11 | #[test] 12 | fn nbt_nonempty() { 13 | let mut nbt = Blob::new(); 14 | nbt.insert("name", "Herobrine").unwrap(); 15 | nbt.insert("health", 100i8).unwrap(); 16 | nbt.insert("food", 20.0f32).unwrap(); 17 | nbt.insert("emeralds", 12345i16).unwrap(); 18 | nbt.insert("timestamp", 1424778774i32).unwrap(); 19 | 20 | #[rustfmt::skip] 21 | let bytes = vec![ 22 | 0x0a, 23 | 0x00, 0x00, 24 | 0x08, 25 | 0x00, 0x04, 26 | 0x6e, 0x61, 0x6d, 0x65, 27 | 0x00, 0x09, 28 | 0x48, 0x65, 0x72, 0x6f, 0x62, 0x72, 0x69, 0x6e, 0x65, 29 | 0x01, 30 | 0x00, 0x06, 31 | 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 32 | 0x64, 33 | 0x05, 34 | 0x00, 0x04, 35 | 0x66, 0x6f, 0x6f, 0x64, 36 | 0x41, 0xa0, 0x00, 0x00, 37 | 0x02, 38 | 0x00, 0x08, 39 | 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x64, 0x73, 40 | 0x30, 0x39, 41 | 0x03, 42 | 0x00, 0x09, 43 | 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 44 | 0x54, 0xec, 0x66, 0x16, 45 | 0x00 46 | ]; 47 | 48 | // Test correct length and contents when field order is preserved 49 | let mut dst = Vec::new(); 50 | nbt.to_writer(&mut dst).unwrap(); 51 | assert_eq!(&bytes.len(), &dst.len()); 52 | #[cfg(feature = "preserve_order")] 53 | assert_eq!(&bytes, &dst); 54 | 55 | // When not using the preserve_order feature, 56 | // we can only test if the decoded bytes match, since the HashMap does 57 | // not guarantee order (and so encoding is likely to be different, but 58 | // still correct). 59 | let mut src = io::Cursor::new(bytes); 60 | let file = Blob::from_reader(&mut src).unwrap(); 61 | assert_eq!(&file, &nbt); 62 | } 63 | 64 | #[test] 65 | fn nbt_empty_nbtfile() { 66 | let nbt = Blob::new(); 67 | 68 | #[rustfmt::skip] 69 | let bytes = vec![ 70 | 0x0a, 71 | 0x00, 0x00, 72 | 0x00 73 | ]; 74 | 75 | // Test encoding. 76 | let mut dst = Vec::new(); 77 | nbt.to_writer(&mut dst).unwrap(); 78 | assert_eq!(&dst, &bytes); 79 | 80 | // Test decoding. 81 | let mut src = io::Cursor::new(bytes); 82 | let file = Blob::from_reader(&mut src).unwrap(); 83 | assert_eq!(&file, &nbt); 84 | } 85 | 86 | #[test] 87 | fn nbt_nested_compound() { 88 | let mut inner = Map::new(); 89 | inner.insert("test".to_string(), Value::Byte(123)); 90 | let mut nbt = Blob::new(); 91 | nbt.insert("inner", Value::Compound(inner)).unwrap(); 92 | 93 | #[rustfmt::skip] 94 | let bytes = vec![ 95 | 0x0a, 96 | 0x00, 0x00, 97 | 0x0a, 98 | 0x00, 0x05, 99 | 0x69, 0x6e, 0x6e, 0x65, 0x72, 100 | 0x01, 101 | 0x00, 0x04, 102 | 0x74, 0x65, 0x73, 0x74, 103 | 0x7b, 104 | 0x00, 105 | 0x00 106 | ]; 107 | 108 | // Test encoding. 109 | let mut dst = Vec::new(); 110 | nbt.to_writer(&mut dst).unwrap(); 111 | assert_eq!(&dst, &bytes); 112 | 113 | // Test decoding. 114 | let mut src = io::Cursor::new(bytes); 115 | let file = Blob::from_reader(&mut src).unwrap(); 116 | assert_eq!(&file, &nbt); 117 | } 118 | 119 | #[test] 120 | fn nbt_empty_list() { 121 | let mut nbt = Blob::new(); 122 | nbt.insert("list", Value::List(Vec::new())).unwrap(); 123 | 124 | #[rustfmt::skip] 125 | let bytes = vec![ 126 | 0x0a, 127 | 0x00, 0x00, 128 | 0x09, 129 | 0x00, 0x04, 130 | 0x6c, 0x69, 0x73, 0x74, 131 | 0x00, 132 | 0x00, 0x00, 0x00, 0x00, 133 | 0x00 134 | ]; 135 | 136 | // Test encoding. 137 | let mut dst = Vec::new(); 138 | nbt.to_writer(&mut dst).unwrap(); 139 | assert_eq!(&dst, &bytes); 140 | 141 | // Test decoding. 142 | let mut src = io::Cursor::new(bytes); 143 | let file = Blob::from_reader(&mut src).unwrap(); 144 | assert_eq!(&file, &nbt); 145 | } 146 | 147 | #[test] 148 | fn nbt_nested_list() { 149 | let mut nbt = Blob::new(); 150 | let inner_one = Value::List(vec![Value::Short(1), Value::Short(2)]); 151 | let inner_two = Value::List(vec![Value::Float(0.25), Value::Float(0.75)]); 152 | nbt.insert("list", Value::List(vec![inner_one, inner_two])) 153 | .unwrap(); 154 | 155 | #[rustfmt::skip] 156 | let bytes = vec![ 157 | 0x0a, 158 | 0x00, 0x00, 159 | 0x09, 160 | 0x00, 0x04, 161 | 0x6c, 0x69, 0x73, 0x74, 162 | 0x09, // Also a list. 163 | 0x00, 0x00, 0x00, 0x02, 164 | 0x02, // First list has type short. 165 | 0x00, 0x00, 0x00, 0x02, 166 | 0x00, 0x01, 0x00, 0x02, 167 | 0x05, // Second list has type float. 168 | 0x00, 0x00, 0x00, 0x02, 169 | 0x3e, 0x80, 0x00, 0x00, 170 | 0x3f, 0x40, 0x00, 0x00, 171 | 0x00 172 | ]; 173 | 174 | // Test encoding. 175 | let mut dst = Vec::new(); 176 | nbt.to_writer(&mut dst).unwrap(); 177 | assert_eq!(&dst, &bytes); 178 | 179 | // Test decoding. 180 | let mut src = io::Cursor::new(bytes); 181 | let file = Blob::from_reader(&mut src).unwrap(); 182 | assert_eq!(&file, &nbt); 183 | } 184 | 185 | #[test] 186 | fn nbt_no_root() { 187 | let bytes = vec![0x00]; 188 | // Will fail, because the root is not a compound. 189 | assert_eq!( 190 | Blob::from_reader(&mut io::Cursor::new(&bytes[..])), 191 | Err(Error::NoRootCompound) 192 | ); 193 | } 194 | 195 | #[test] 196 | fn nbt_no_end_tag() { 197 | #[rustfmt::skip] 198 | let bytes = vec![ 199 | 0x0a, 200 | 0x00, 0x00, 201 | 0x09, 202 | 0x00, 0x04, 203 | 0x6c, 0x69, 0x73, 0x74, 204 | 0x01, 205 | 0x00, 0x00, 0x00, 0x00 206 | ]; 207 | 208 | // Will fail, because there is no end tag. 209 | assert_eq!( 210 | Blob::from_reader(&mut io::Cursor::new(&bytes[..])), 211 | Err(Error::IncompleteNbtValue) 212 | ); 213 | } 214 | 215 | #[test] 216 | fn nbt_invalid_id() { 217 | #[rustfmt::skip] 218 | let bytes = vec![ 219 | 0x0a, 220 | 0x00, 0x00, 221 | 0x0f, // No tag associated with 0x0f. 222 | 0x00, 0x04, 223 | 0x6c, 0x69, 0x73, 0x74, 224 | 0x01, 225 | 0x00 226 | ]; 227 | assert_eq!( 228 | Blob::from_reader(&mut io::Cursor::new(&bytes[..])), 229 | Err(Error::InvalidTypeId(15)) 230 | ); 231 | } 232 | 233 | #[test] 234 | fn nbt_invalid_list() { 235 | let mut nbt = Blob::new(); 236 | let mut badlist = Vec::new(); 237 | badlist.push(Value::Byte(1)); 238 | badlist.push(Value::Short(1)); 239 | // Will fail to insert, because the List is heterogeneous. 240 | assert_eq!( 241 | nbt.insert("list", Value::List(badlist)), 242 | Err(Error::HeterogeneousList) 243 | ); 244 | } 245 | 246 | #[test] 247 | fn nbt_bad_compression() { 248 | // These aren't in the zlib or gzip format, so they'll fail. 249 | let bytes = vec![0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]; 250 | assert!(Blob::from_gzip_reader(&mut io::Cursor::new(&bytes[..])).is_err()); 251 | assert!(Blob::from_zlib_reader(&mut io::Cursor::new(&bytes[..])).is_err()); 252 | } 253 | 254 | #[test] 255 | fn nbt_compression() { 256 | // Create a non-trivial Blob. 257 | let mut nbt = Blob::new(); 258 | nbt.insert("name", Value::String("Herobrine".to_string())) 259 | .unwrap(); 260 | nbt.insert("health", Value::Byte(100)).unwrap(); 261 | nbt.insert("food", Value::Float(20.0)).unwrap(); 262 | nbt.insert("emeralds", Value::Short(12345)).unwrap(); 263 | nbt.insert("timestamp", Value::Int(1424778774)).unwrap(); 264 | 265 | // Test zlib encoding/decoding. 266 | let mut zlib_dst = Vec::new(); 267 | nbt.to_zlib_writer(&mut zlib_dst).unwrap(); 268 | let zlib_file = Blob::from_zlib_reader(&mut io::Cursor::new(zlib_dst)).unwrap(); 269 | assert_eq!(&nbt, &zlib_file); 270 | 271 | // Test gzip encoding/decoding. 272 | let mut gzip_dst = Vec::new(); 273 | nbt.to_gzip_writer(&mut gzip_dst).unwrap(); 274 | let gz_file = Blob::from_gzip_reader(&mut io::Cursor::new(gzip_dst)).unwrap(); 275 | assert_eq!(&nbt, &gz_file); 276 | } 277 | 278 | #[test] 279 | fn nbt_bigtest() { 280 | let mut bigtest_file = File::open("tests/big1.nbt").unwrap(); 281 | let bigtest = Blob::from_gzip_reader(&mut bigtest_file).unwrap(); 282 | // This is a pretty indirect way of testing correctness. 283 | let mut dst = Vec::new(); 284 | bigtest.to_writer(&mut dst).unwrap(); 285 | assert_eq!(1544, dst.len()); 286 | } 287 | 288 | #[test] 289 | fn nbt_arrays() { 290 | let mut arrays_file = File::open("tests/arrays.nbt").unwrap(); 291 | let arrays = Blob::from_reader(&mut arrays_file).unwrap(); 292 | match &arrays["ia"] { 293 | &Value::IntArray(ref arr) => assert_eq!(&[-2, -1, 0, 1, 2], &**arr), 294 | _ => panic!("ia was not TAG_IntArray"), 295 | } 296 | 297 | match &arrays["ba"] { 298 | &Value::ByteArray(ref arr) => assert_eq!(&[-2, -1, 0, 1, 2], &**arr), 299 | _ => panic!("ba was not TAG_ByteArray"), 300 | } 301 | 302 | match &arrays["la"] { 303 | &Value::LongArray(ref arr) => assert_eq!(&[-2, -1, 0, 1, 2], &**arr), 304 | _ => panic!("la was not TAG_LongArray"), 305 | } 306 | } 307 | 308 | #[test] 309 | #[cfg(feature = "serde")] 310 | fn serde_blob() { 311 | use de::from_reader; 312 | use ser::to_writer; 313 | 314 | let mut nbt = Blob::new(); 315 | nbt.insert("name", "Herobrine").unwrap(); 316 | nbt.insert("health", 100i8).unwrap(); 317 | nbt.insert("food", 20.0f32).unwrap(); 318 | nbt.insert("emeralds", 12345i16).unwrap(); 319 | nbt.insert("timestamp", 1424778774i32).unwrap(); 320 | 321 | #[rustfmt::skip] 322 | let bytes = vec![ 323 | 0x0a, 324 | 0x00, 0x00, 325 | 0x08, 326 | 0x00, 0x04, 327 | 0x6e, 0x61, 0x6d, 0x65, 328 | 0x00, 0x09, 329 | 0x48, 0x65, 0x72, 0x6f, 0x62, 0x72, 0x69, 0x6e, 0x65, 330 | 0x01, 331 | 0x00, 0x06, 332 | 0x68, 0x65, 0x61, 0x6c, 0x74, 0x68, 333 | 0x64, 334 | 0x05, 335 | 0x00, 0x04, 336 | 0x66, 0x6f, 0x6f, 0x64, 337 | 0x41, 0xa0, 0x00, 0x00, 338 | 0x02, 339 | 0x00, 0x08, 340 | 0x65, 0x6d, 0x65, 0x72, 0x61, 0x6c, 0x64, 0x73, 341 | 0x30, 0x39, 342 | 0x03, 343 | 0x00, 0x09, 344 | 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 345 | 0x54, 0xec, 0x66, 0x16, 346 | 0x00 347 | ]; 348 | 349 | // Roundtrip. 350 | 351 | let mut src = io::Cursor::new(bytes.clone()); 352 | let file: Blob = from_reader(&mut src).unwrap(); 353 | assert_eq!(&file, &nbt); 354 | let mut dst = Vec::new(); 355 | to_writer(&mut dst, &nbt, None).unwrap(); 356 | // When the preserve_order feature is not enabled, 357 | // we can only test if the decoded bytes match, since the HashMap does 358 | // not guarantee order (and so encoding is likely to be different, but 359 | // still correct). 360 | assert_eq!(&bytes.len(), &dst.len()); 361 | #[cfg(feature = "preserve_order")] 362 | assert_eq!(&bytes, &dst); 363 | } 364 | 365 | #[test] 366 | fn nbt_modified_utf8() { 367 | let mut nbt = Blob::new(); 368 | // These strings are taken from the cesu8 documentation. 369 | nbt.insert("\u{10401}", "\0\0").unwrap(); 370 | 371 | #[rustfmt::skip] 372 | let bytes = vec![ 373 | 0x0a, 374 | 0x00, 0x00, 375 | 0x08, 376 | 0x00, 0x06, 377 | 0xed, 0xa0, 0x81, 0xed, 0xb0, 0x81, 378 | 0x00, 0x04, 379 | 0xc0, 0x80, 0xc0, 0x80, 380 | 0x00 381 | ]; 382 | 383 | // Test encoding. 384 | let mut dst = Vec::new(); 385 | nbt.to_writer(&mut dst).unwrap(); 386 | assert_eq!(&dst, &bytes); 387 | 388 | // Test decoding. 389 | let mut src = io::Cursor::new(bytes); 390 | let file = Blob::from_reader(&mut src).unwrap(); 391 | assert_eq!(&file, &nbt); 392 | } 393 | 394 | #[test] 395 | fn nbt_sizes() { 396 | // Arbitarary values, covering most data types 397 | let mut subtree = Map::::new(); 398 | subtree.insert("name".into(), "Herobrine".into()); 399 | subtree.insert("health".into(), 100i8.into()); 400 | subtree.insert("enormous".into(), 100i64.into()); 401 | subtree.insert("food".into(), 20.0f32.into()); 402 | subtree.insert("emeralds".into(), 12345i16.into()); 403 | subtree.insert("timestamp".into(), 1424778774i32.into()); 404 | subtree.insert( 405 | "list".into(), 406 | Value::List(vec![1, 2, 3, 4].into_iter().map(Value::Int).collect()), 407 | ); 408 | 409 | let deeper_sub = Value::Compound(subtree.clone()); 410 | 411 | subtree.insert("recursion".into(), deeper_sub); 412 | 413 | let subling_sub = Value::Compound(subtree.clone()); 414 | let orig_compound = Value::Compound(subtree); 415 | 416 | // Here so this test covers every tag type 417 | let byte_array = Value::ByteArray((-127..127).collect()); 418 | let int_array = Value::IntArray((0..128).collect()); 419 | let long_array = Value::LongArray((0..512).collect()); 420 | 421 | // Creating a blob that has weird nested compounds/lists/arrays 422 | // Intended to cover all possible tags and make sure recursions 423 | // handle nested types correctly. 424 | let mut root = Blob::new(); 425 | root.insert("List-C", Value::List(vec![orig_compound, subling_sub])) 426 | .unwrap(); 427 | root.insert("List-B", byte_array).unwrap(); 428 | root.insert("List-I", int_array).unwrap(); 429 | root.insert("List-L", long_array).unwrap(); 430 | 431 | // Write out the blob 432 | let mut cursor = std::io::Cursor::new(vec![]); 433 | root.to_writer(&mut cursor).unwrap(); 434 | 435 | assert_eq!(cursor.position() as usize, root.len_bytes()); 436 | } 437 | -------------------------------------------------------------------------------- /tests/serde_basics.rs: -------------------------------------------------------------------------------- 1 | #[macro_use] 2 | extern crate serde_derive; 3 | extern crate serde; 4 | 5 | extern crate nbt; 6 | 7 | use std::collections::HashMap; 8 | 9 | use serde::{Serialize, Serializer}; 10 | 11 | /// Helper function that asserts data of type T can be serialized into and 12 | /// deserialized from `bytes`. `name` is an optional header for the top-level 13 | /// NBT compound. 14 | fn assert_roundtrip_eq(nbt: T, bytes: &[u8], name: Option<&str>) 15 | where 16 | for<'de> T: serde::Serialize + serde::Deserialize<'de> + PartialEq + std::fmt::Debug, 17 | { 18 | let mut dst = Vec::with_capacity(bytes.len()); 19 | 20 | nbt::ser::to_writer(&mut dst, &nbt, name).expect("NBT serialization."); 21 | assert_eq!(bytes, &dst[..]); 22 | 23 | let read: T = nbt::de::from_reader(bytes).expect("NBT deserialization."); 24 | assert_eq!(read, nbt); 25 | } 26 | 27 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 28 | struct ByteNbt { 29 | data: i8, 30 | } 31 | 32 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 33 | struct PrimitiveNbt { 34 | byte: i8, 35 | short: i16, 36 | int: i32, 37 | long: i64, 38 | float: f32, 39 | double: f64, 40 | string: String, 41 | } 42 | 43 | #[test] 44 | fn roundtrip_primitives() { 45 | let nbt = PrimitiveNbt { 46 | byte: 100, 47 | short: 100, 48 | int: 100, 49 | long: 100, 50 | float: 20.0, 51 | double: 20.0, 52 | string: "Herobrine".to_string(), 53 | }; 54 | 55 | #[rustfmt::skip] 56 | let bytes = vec![ 57 | 0x0a, 58 | 0x00, 0x04, // Header: "data" 59 | 0x64, 0x61, 0x74, 0x61, 60 | 0x01, 61 | 0x00, 0x04, 62 | 0x62, 0x79, 0x74, 0x65, 63 | 0x64, 64 | 0x02, 65 | 0x00, 0x05, 66 | 0x73, 0x68, 0x6f, 0x72, 0x74, 67 | 0x00, 0x64, 68 | 0x03, 69 | 0x00, 0x03, 70 | 0x69, 0x6e, 0x74, 71 | 0x00, 0x00, 0x00, 0x64, 72 | 0x04, 73 | 0x00, 0x04, 74 | 0x6c, 0x6f, 0x6e, 0x67, 75 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 76 | 0x05, 77 | 0x00, 0x05, 78 | 0x66, 0x6c, 0x6f, 0x61, 0x74, 79 | 0x41, 0xa0, 0x00, 0x00, 80 | 0x06, 81 | 0x00, 0x06, 82 | 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 83 | 0x40, 0x34, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 84 | 0x08, 85 | 0x00, 0x06, 86 | 0x73, 0x74, 0x72, 0x69, 0x6e, 0x67, 87 | 0x00, 0x09, 88 | 0x48, 0x65, 0x72, 0x6f, 0x62, 0x72, 0x69, 0x6e, 0x65, 89 | 0x00 90 | ]; 91 | 92 | assert_roundtrip_eq(nbt, &bytes, Some("data")); 93 | } 94 | 95 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 96 | struct BasicListNbt { 97 | data: Vec, 98 | } 99 | 100 | #[test] 101 | fn roundtrip_basic_list() { 102 | let nbt = BasicListNbt { 103 | data: vec![1, 2, 3], 104 | }; 105 | 106 | #[rustfmt::skip] 107 | let bytes = vec![ 108 | 0x0a, 109 | 0x00, 0x00, 110 | 0x09, 111 | 0x00, 0x04, 112 | 0x64, 0x61, 0x74, 0x61, 113 | 0x02, // List type. 114 | 0x00, 0x00, 0x00, 0x03, // Length. 115 | 0x00, 0x01, 116 | 0x00, 0x02, 117 | 0x00, 0x03, 118 | 0x00 119 | ]; 120 | 121 | assert_roundtrip_eq(nbt, &bytes, None); 122 | } 123 | 124 | #[test] 125 | fn roundtrip_empty_list() { 126 | let nbt = BasicListNbt { data: vec![] }; 127 | 128 | #[rustfmt::skip] 129 | let bytes = vec![ 130 | 0x0a, 131 | 0x00, 0x00, 132 | 0x09, 133 | 0x00, 0x04, 134 | 0x64, 0x61, 0x74, 0x61, 135 | 0x00, // Empty list type. 136 | 0x00, 0x00, 0x00, 0x00, // Length. 137 | 0x00 138 | ]; 139 | 140 | assert_roundtrip_eq(nbt, &bytes, None); 141 | } 142 | 143 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 144 | struct NestedListNbt { 145 | data: Vec>, 146 | } 147 | 148 | #[test] 149 | fn roundtrip_nested_list() { 150 | let nbt = NestedListNbt { 151 | data: vec![vec![1, 2], vec![3, 4]], 152 | }; 153 | 154 | #[rustfmt::skip] 155 | let bytes = vec![ 156 | 0x0a, 157 | 0x00, 0x00, 158 | 0x09, 159 | 0x00, 0x04, 160 | 0x64, 0x61, 0x74, 0x61, 161 | 0x09, // Also a list. 162 | 0x00, 0x00, 0x00, 0x02, 163 | 0x02, // First list has type short. 164 | 0x00, 0x00, 0x00, 0x02, 165 | 0x00, 0x01, 0x00, 0x02, 166 | 0x02, // Second list has type short. 167 | 0x00, 0x00, 0x00, 0x02, 168 | 0x00, 0x03, 0x00, 0x04, 169 | 0x00 170 | ]; 171 | 172 | assert_roundtrip_eq(nbt, &bytes, None); 173 | } 174 | 175 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 176 | struct NestedArrayNbt { 177 | #[serde(serialize_with = "nested_i32_array")] 178 | data: Vec>, 179 | } 180 | 181 | fn nested_i32_array(outer_arr: &Vec>, serializer: S) -> Result 182 | where 183 | S: Serializer, 184 | { 185 | #[derive(Debug)] 186 | struct Wrapper<'a>(&'a Vec); 187 | 188 | impl<'a> Serialize for Wrapper<'a> { 189 | fn serialize(&self, serializer: S) -> Result { 190 | nbt::i32_array(self.0, serializer) 191 | } 192 | } 193 | 194 | serializer.collect_seq(outer_arr.iter().map(|vec| Wrapper(vec))) 195 | } 196 | 197 | #[test] 198 | fn roundtrip_nested_array() { 199 | let nbt = NestedArrayNbt { 200 | data: vec![vec![1, 2], vec![3, 4]], 201 | }; 202 | 203 | #[rustfmt::skip] 204 | let bytes = vec![ 205 | 0x0a, 206 | 0x00, 0x00, 207 | 0x09, 208 | 0x00, 0x04, 209 | 0x64, 0x61, 0x74, 0x61, 210 | 0x0b, // Int array. 211 | 0x00, 0x00, 0x00, 0x02, 212 | // First array. 213 | 0x00, 0x00, 0x00, 0x02, 214 | 0x00, 0x00, 0x00, 0x01, 215 | 0x00, 0x00, 0x00, 0x02, 216 | // Second array. 217 | 0x00, 0x00, 0x00, 0x02, 218 | 0x00, 0x00, 0x00, 0x03, 219 | 0x00, 0x00, 0x00, 0x04, 220 | 0x00 221 | ]; 222 | 223 | assert_roundtrip_eq(nbt, &bytes, None); 224 | } 225 | 226 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 227 | struct ByteArrayNbt { 228 | #[serde(serialize_with = "nbt::i8_array")] 229 | data: Vec, 230 | } 231 | 232 | #[test] 233 | fn roundtrip_byte_array() { 234 | let nbt = ByteArrayNbt { 235 | data: vec![1, 2, 3], 236 | }; 237 | 238 | #[rustfmt::skip] 239 | let bytes = vec![ 240 | 0x0a, 241 | 0x00, 0x00, 242 | 0x07, 243 | 0x00, 0x04, 244 | 0x64, 0x61, 0x74, 0x61, 245 | 0x00, 0x00, 0x00, 0x03, // Length. 246 | 0x01, 0x02, 0x03, // Content. 247 | 0x00 248 | ]; 249 | 250 | assert_roundtrip_eq(nbt, &bytes, None); 251 | } 252 | 253 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 254 | struct IntArrayNbt { 255 | #[serde(serialize_with = "nbt::i32_array")] 256 | data: Vec, 257 | } 258 | 259 | #[test] 260 | fn roundtrip_empty_array() { 261 | let nbt = IntArrayNbt { data: vec![] }; 262 | 263 | #[rustfmt::skip] 264 | let bytes = vec![ 265 | 0x0a, 266 | 0x00, 0x00, 267 | 0x0b, 268 | 0x00, 0x04, 269 | 0x64, 0x61, 0x74, 0x61, 270 | 0x00, 0x00, 0x00, 0x00, // Length. 271 | 0x00 272 | ]; 273 | 274 | assert_roundtrip_eq(nbt, &bytes, None); 275 | } 276 | 277 | #[test] 278 | fn roundtrip_int_array() { 279 | let nbt = IntArrayNbt { 280 | data: vec![1, 2, 3], 281 | }; 282 | 283 | #[rustfmt::skip] 284 | let bytes = vec![ 285 | 0x0a, 286 | 0x00, 0x00, 287 | 0x0b, 288 | 0x00, 0x04, 289 | 0x64, 0x61, 0x74, 0x61, 290 | 0x00, 0x00, 0x00, 0x03, // Length. 291 | // Content. 292 | 0x00, 0x00, 0x00, 0x01, 293 | 0x00, 0x00, 0x00, 0x02, 294 | 0x00, 0x00, 0x00, 0x03, 295 | 0x00 296 | ]; 297 | 298 | assert_roundtrip_eq(nbt, &bytes, None); 299 | } 300 | 301 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 302 | struct LongArrayNbt { 303 | #[serde(serialize_with = "nbt::ser::i64_array")] 304 | data: Vec, 305 | } 306 | 307 | #[test] 308 | fn roundtrip_long_array() { 309 | let nbt = LongArrayNbt { 310 | data: vec![1, 2, 3], 311 | }; 312 | 313 | #[rustfmt::skip] 314 | let bytes = vec![ 315 | 0x0a, 316 | 0x00, 0x00, 317 | 0x0c, 318 | 0x00, 0x04, 319 | 0x64, 0x61, 0x74, 0x61, 320 | 0x00, 0x00, 0x00, 0x03, // Length. 321 | // Content. 322 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 323 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 324 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 325 | 0x00 326 | ]; 327 | 328 | assert_roundtrip_eq(nbt, &bytes, None); 329 | } 330 | 331 | #[derive(Debug, PartialEq, Serialize)] 332 | struct CustomSerializerArrayNbt { 333 | #[serde(serialize_with = "shift_right_serializer")] 334 | data: Vec, 335 | } 336 | 337 | // We want to serialize an i16 vector as a ByteArray by shifting every element right by 8 bits 338 | fn shift_right_serializer(original_array: &Vec, serializer: S) -> Result 339 | where 340 | S: Serializer, 341 | { 342 | nbt::i8_array(original_array.iter().map(|&i| (i >> 8) as i8), serializer) 343 | } 344 | 345 | #[test] 346 | fn serialize_custom_serializer_array() { 347 | let nbt = CustomSerializerArrayNbt { 348 | data: vec![0xAABBu16 as i16, 0x3400, 0x1234, 0x0012], 349 | }; 350 | 351 | #[rustfmt::skip] 352 | let bytes = vec![ 353 | 0x0a, 354 | 0x00, 0x00, 355 | 0x07, 356 | 0x00, 0x04, 357 | 0x64, 0x61, 0x74, 0x61, 358 | 0x00, 0x00, 0x00, 0x04, // Length. 359 | 0xAA, 0x34, 0x12, 0x00, // Content. 360 | 0x00 361 | ]; 362 | 363 | let mut dst = Vec::with_capacity(bytes.len()); 364 | 365 | nbt::ser::to_writer(&mut dst, &nbt, None).expect("NBT serialization."); 366 | assert_eq!(bytes, &dst[..]); 367 | } 368 | 369 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 370 | struct BoolNbt { 371 | data: bool, 372 | } 373 | 374 | #[test] 375 | fn roundtrip_bool() { 376 | let nbt = BoolNbt { data: true }; 377 | 378 | #[rustfmt::skip] 379 | let bytes = vec![ 380 | 0x0a, 381 | 0x00, 0x00, 382 | 0x01, 383 | 0x00, 0x04, 384 | 0x64, 0x61, 0x74, 0x61, 385 | 0x01, 386 | 0x00 387 | ]; 388 | 389 | assert_roundtrip_eq(nbt, &bytes, None); 390 | } 391 | 392 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 393 | struct OptionNbt { 394 | data: Option, 395 | } 396 | 397 | #[test] 398 | fn roundtrip_some() { 399 | let nbt = OptionNbt { data: Some(100) }; 400 | 401 | #[rustfmt::skip] 402 | let bytes = vec![ 403 | 0x0a, 404 | 0x00, 0x00, 405 | 0x01, 406 | 0x00, 0x04, 407 | 0x64, 0x61, 0x74, 0x61, 408 | 0x64, 409 | 0x00 410 | ]; 411 | 412 | assert_roundtrip_eq(nbt, &bytes, None); 413 | } 414 | 415 | #[test] 416 | fn roundtrip_none() { 417 | let nbt = OptionNbt { data: None }; 418 | 419 | #[rustfmt::skip] 420 | let bytes = vec![ 421 | 0x0a, 422 | 0x00, 0x00, 423 | // Not included. 424 | 0x00 425 | ]; 426 | 427 | assert_roundtrip_eq(nbt, &bytes, None); 428 | } 429 | 430 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 431 | struct UnitStructNbt; 432 | 433 | #[test] 434 | fn roundtrip_unit_struct() { 435 | let nbt = UnitStructNbt; 436 | 437 | #[rustfmt::skip] 438 | let bytes = vec![ 439 | 0x0a, 440 | 0x00, 0x00, 441 | 0x00 442 | ]; 443 | 444 | assert_roundtrip_eq(nbt, &bytes, None); 445 | } 446 | 447 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 448 | struct NewByteNbt(ByteNbt); 449 | 450 | #[test] 451 | fn roundtrip_newtype_struct() { 452 | let nbt = NewByteNbt(ByteNbt { data: 100 }); 453 | 454 | #[rustfmt::skip] 455 | let bytes = vec![ 456 | 0x0a, 457 | 0x00, 0x00, 458 | 0x01, 459 | 0x00, 0x04, 460 | 0x64, 0x61, 0x74, 0x61, 461 | 0x64, 462 | 0x00 463 | ]; 464 | 465 | assert_roundtrip_eq(nbt, &bytes, None); 466 | } 467 | 468 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 469 | struct NestedByteNbt { 470 | data: ByteNbt, 471 | } 472 | 473 | #[test] 474 | fn roundtrip_nested() { 475 | let nbt = NestedByteNbt { 476 | data: ByteNbt { data: 100 }, 477 | }; 478 | 479 | #[rustfmt::skip] 480 | let bytes = vec![ 481 | 0x0a, 482 | 0x00, 0x00, 483 | 0x0a, 484 | 0x00, 0x04, 485 | 0x64, 0x61, 0x74, 0x61, 486 | 0x01, 487 | 0x00, 0x04, 488 | 0x64, 0x61, 0x74, 0x61, 489 | 0x64, 490 | 0x00, 491 | 0x00 492 | ]; 493 | 494 | assert_roundtrip_eq(nbt, &bytes, None); 495 | } 496 | 497 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 498 | struct NestedUnitStructNbt { 499 | data: UnitStructNbt, 500 | } 501 | 502 | #[test] 503 | fn roundtrip_nested_unit_struct() { 504 | let nbt = NestedUnitStructNbt { 505 | data: UnitStructNbt, 506 | }; 507 | 508 | #[rustfmt::skip] 509 | let bytes = vec![ 510 | 0x0a, 511 | 0x00, 0x00, 512 | 0x0a, 513 | 0x00, 0x04, 514 | 0x64, 0x61, 0x74, 0x61, 515 | // No content. 516 | 0x00, 517 | 0x00 518 | ]; 519 | 520 | assert_roundtrip_eq(nbt, &bytes, None); 521 | } 522 | 523 | #[derive(Debug, PartialEq, Serialize, Deserialize)] 524 | struct NestedNewByteNbt { 525 | data: NewByteNbt, 526 | } 527 | 528 | #[test] 529 | fn roundtrip_nested_newtype_struct() { 530 | let nbt = NestedNewByteNbt { 531 | data: NewByteNbt(ByteNbt { data: 100 }), 532 | }; 533 | 534 | #[rustfmt::skip] 535 | let bytes = vec![ 536 | 0x0a, 537 | 0x00, 0x00, 538 | 0x0a, 539 | 0x00, 0x04, 540 | 0x64, 0x61, 0x74, 0x61, 541 | 0x01, 542 | 0x00, 0x04, 543 | 0x64, 0x61, 0x74, 0x61, 544 | 0x64, 545 | 0x00, 546 | 0x00 547 | ]; 548 | 549 | assert_roundtrip_eq(nbt, &bytes, None); 550 | } 551 | 552 | #[test] 553 | fn roundtrip_hashmap() { 554 | let mut nbt = HashMap::new(); 555 | nbt.insert("data".to_string(), 100i8); 556 | 557 | #[rustfmt::skip] 558 | let bytes = vec![ 559 | 0x0a, 560 | 0x00, 0x00, 561 | 0x01, 562 | 0x00, 0x04, 563 | 0x64, 0x61, 0x74, 0x61, 564 | 0x64, 565 | 0x00 566 | ]; 567 | 568 | assert_roundtrip_eq(nbt, &bytes, None); 569 | } 570 | 571 | #[test] 572 | fn ser_blob_array() { 573 | let mut blob = nbt::Blob::new(); 574 | blob.insert("larr", nbt::Value::LongArray(vec![456, 123])).unwrap(); 575 | blob.insert("iarr", nbt::Value::IntArray(vec![123, 456])).unwrap(); 576 | 577 | #[rustfmt::skip] 578 | let bytes = vec![ 579 | 0x0a, 580 | 0x00, 0x00, 581 | 0x0c, 582 | 0x00, 0x04, 583 | 0x6c, 0x61, 0x72, 0x72, 584 | 0x00, 0x00, 0x00, 0x02, 585 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0xc8, 586 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7b, 587 | 0x0b, 588 | 0x00, 0x04, 589 | 0x69, 0x61, 0x72, 0x72, 590 | 0x00, 0x00, 0x00, 0x02, 591 | 0x00, 0x00, 0x00, 0x7b, 592 | 0x00, 0x00, 0x01, 0xc8, 593 | 0x00 594 | ]; 595 | 596 | let mut dst = Vec::with_capacity(bytes.len()); 597 | 598 | nbt::ser::to_writer(&mut dst, &blob, None).expect("NBT serialization."); 599 | assert_eq!(bytes, &dst[..]); 600 | } 601 | -------------------------------------------------------------------------------- /src/ser.rs: -------------------------------------------------------------------------------- 1 | //! Serialize a Rust data structure into Named Binary Tag data. 2 | 3 | use std::io; 4 | 5 | use flate2::write::{GzEncoder, ZlibEncoder}; 6 | use flate2::Compression; 7 | use serde; 8 | use serde::ser; 9 | 10 | use raw; 11 | 12 | use error::{Error, Result}; 13 | use serde::ser::Error as SerError; 14 | 15 | /// Encode `value` in Named Binary Tag format to the given `io::Write` 16 | /// destination, with an optional header. 17 | #[inline] 18 | pub fn to_writer<'a, W, T>(dst: &mut W, value: &T, header: Option<&'a str>) -> Result<()> 19 | where 20 | W: ?Sized + io::Write, 21 | T: ?Sized + ser::Serialize, 22 | { 23 | let mut encoder = Encoder::new(dst, header); 24 | value.serialize(&mut encoder) 25 | } 26 | 27 | /// Encode `value` in Named Binary Tag format to the given `io::Write` 28 | /// destination, with an optional header. 29 | pub fn to_gzip_writer<'a, W, T>(dst: &mut W, value: &T, header: Option<&'a str>) -> Result<()> 30 | where 31 | W: ?Sized + io::Write, 32 | T: ?Sized + ser::Serialize, 33 | { 34 | let mut encoder = Encoder::new(GzEncoder::new(dst, Compression::default()), header); 35 | value.serialize(&mut encoder) 36 | } 37 | 38 | /// Encode `value` in Named Binary Tag format to the given `io::Write` 39 | /// destination, with an optional header. 40 | pub fn to_zlib_writer<'a, W, T>(dst: &mut W, value: &T, header: Option<&'a str>) -> Result<()> 41 | where 42 | W: ?Sized + io::Write, 43 | T: ?Sized + ser::Serialize, 44 | { 45 | let mut encoder = Encoder::new(ZlibEncoder::new(dst, Compression::default()), header); 46 | value.serialize(&mut encoder) 47 | } 48 | 49 | /// Encode objects to Named Binary Tag format. 50 | /// 51 | /// This structure can be used to serialize objects which implement the 52 | /// `serde::Serialize` trait into NBT format. Note that not all types are 53 | /// representable in NBT format (notably unsigned integers), so this encoder may 54 | /// return errors. 55 | pub struct Encoder<'a, W> { 56 | writer: W, 57 | header: Option<&'a str>, 58 | } 59 | 60 | impl<'a, W> Encoder<'a, W> 61 | where 62 | W: io::Write, 63 | { 64 | /// Create an encoder with optional `header` from a given Writer. 65 | pub fn new(writer: W, header: Option<&'a str>) -> Self { 66 | Encoder { writer, header } 67 | } 68 | 69 | /// Write the NBT tag and an optional header to the underlying writer. 70 | #[inline] 71 | fn write_header(&mut self, tag: i8, header: Option<&str>) -> Result<()> { 72 | raw::write_bare_byte(&mut self.writer, tag)?; 73 | match header { 74 | None => raw::write_bare_short(&mut self.writer, 0).map_err(From::from), 75 | Some(h) => raw::write_bare_string(&mut self.writer, h).map_err(From::from), 76 | } 77 | } 78 | } 79 | 80 | /// "Inner" version of the NBT encoder, capable of serializing bare types. 81 | struct InnerEncoder<'a, 'b: 'a, W: 'a> { 82 | outer: &'a mut Encoder<'b, W>, 83 | } 84 | 85 | impl<'a, 'b, W> InnerEncoder<'a, 'b, W> 86 | where 87 | W: io::Write, 88 | { 89 | pub fn from_outer(outer: &'a mut Encoder<'b, W>) -> Self { 90 | InnerEncoder { outer } 91 | } 92 | } 93 | 94 | #[doc(hidden)] 95 | pub struct Compound<'a, 'b: 'a, W: 'a> { 96 | outer: &'a mut Encoder<'b, W>, 97 | length: i32, 98 | sigil: bool, 99 | } 100 | 101 | impl<'a, 'b, W> Compound<'a, 'b, W> 102 | where 103 | W: io::Write, 104 | { 105 | fn from_outer(outer: &'a mut Encoder<'b, W>) -> Self { 106 | Compound { 107 | outer, 108 | length: 0, 109 | sigil: false, 110 | } 111 | } 112 | 113 | fn for_seq(outer: &'a mut Encoder<'b, W>, length: i32, array: bool) -> Result { 114 | if length == 0 || array { 115 | // Write sigil for empty list or typed array, because SerializeSeq::serialize_element is never called 116 | if !array { 117 | // For an empty list, write TAG_End as the tag type. 118 | raw::write_bare_byte(&mut outer.writer, 0x00)?; 119 | } 120 | // Write list/array length 121 | raw::write_bare_int(&mut outer.writer, length)?; 122 | } 123 | Ok(Compound { 124 | outer, 125 | length, 126 | sigil: false, 127 | }) 128 | } 129 | } 130 | 131 | impl<'a, 'b, W> ser::SerializeSeq for Compound<'a, 'b, W> 132 | where 133 | W: io::Write, 134 | { 135 | type Ok = (); 136 | type Error = Error; 137 | 138 | fn serialize_element(&mut self, value: &T) -> Result<()> 139 | where 140 | T: serde::Serialize, 141 | { 142 | if !self.sigil { 143 | value.serialize(&mut TagEncoder::from_outer( 144 | self.outer, 145 | Option::::None, 146 | ))?; 147 | raw::write_bare_int(&mut self.outer.writer, self.length)?; 148 | self.sigil = true; 149 | } 150 | value.serialize(&mut InnerEncoder::from_outer(self.outer)) 151 | } 152 | 153 | fn end(self) -> Result<()> { 154 | Ok(()) 155 | } 156 | } 157 | 158 | impl<'a, 'b, W> ser::SerializeTupleStruct for Compound<'a, 'b, W> 159 | where 160 | W: io::Write, 161 | { 162 | type Ok = (); 163 | type Error = Error; 164 | 165 | fn serialize_field(&mut self, value: &T) -> Result<()> 166 | where 167 | T: serde::Serialize, 168 | { 169 | value.serialize(&mut InnerEncoder::from_outer(self.outer)) 170 | } 171 | 172 | fn end(self) -> Result<()> { 173 | Ok(()) 174 | } 175 | } 176 | 177 | impl<'a, 'b, W> ser::SerializeStruct for Compound<'a, 'b, W> 178 | where 179 | W: io::Write, 180 | { 181 | type Ok = (); 182 | type Error = Error; 183 | 184 | fn serialize_field(&mut self, key: &'static str, value: &T) -> Result<()> 185 | where 186 | T: serde::Serialize, 187 | { 188 | value.serialize(&mut TagEncoder::from_outer(self.outer, Some(key)))?; 189 | value.serialize(&mut InnerEncoder::from_outer(self.outer)) 190 | } 191 | 192 | fn end(self) -> Result<()> { 193 | raw::close_nbt(&mut self.outer.writer) 194 | } 195 | } 196 | 197 | impl<'a, 'b, W> ser::SerializeMap for Compound<'a, 'b, W> 198 | where 199 | W: io::Write, 200 | { 201 | type Ok = (); 202 | type Error = Error; 203 | 204 | fn serialize_key(&mut self, _key: &T) -> Result<()> 205 | where 206 | T: serde::Serialize, 207 | { 208 | unimplemented!() 209 | } 210 | 211 | fn serialize_value(&mut self, _value: &T) -> Result<()> 212 | where 213 | T: serde::Serialize, 214 | { 215 | unimplemented!() 216 | } 217 | 218 | fn serialize_entry(&mut self, key: &K, value: &V) -> Result<()> 219 | where 220 | K: serde::Serialize, 221 | V: serde::Serialize, 222 | { 223 | value.serialize(&mut TagEncoder::from_outer(self.outer, Some(key)))?; 224 | value.serialize(&mut InnerEncoder::from_outer(self.outer)) 225 | } 226 | 227 | fn end(self) -> Result<()> { 228 | raw::close_nbt(&mut self.outer.writer) 229 | } 230 | } 231 | 232 | impl<'a, 'b, W> serde::Serializer for &'a mut Encoder<'b, W> 233 | where 234 | W: io::Write, 235 | { 236 | type Ok = (); 237 | type Error = Error; 238 | type SerializeSeq = ser::Impossible<(), Error>; 239 | type SerializeTuple = ser::Impossible<(), Error>; 240 | type SerializeTupleStruct = ser::Impossible<(), Error>; 241 | type SerializeTupleVariant = ser::Impossible<(), Error>; 242 | type SerializeMap = Compound<'a, 'b, W>; 243 | type SerializeStruct = Compound<'a, 'b, W>; 244 | type SerializeStructVariant = ser::Impossible<(), Error>; 245 | 246 | return_expr_for_serialized_types!( 247 | Err(Error::NoRootCompound); bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 248 | char str bytes none some unit unit_variant newtype_variant 249 | seq tuple tuple_struct tuple_variant struct_variant 250 | ); 251 | 252 | /// Serialize unit structs as empty `Tag_Compound` data. 253 | #[inline] 254 | fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { 255 | let header = self.header; // Circumvent strange borrowing errors. 256 | self.write_header(0x0a, header)?; 257 | raw::close_nbt(&mut self.writer).map_err(From::from) 258 | } 259 | 260 | /// Serialize newtype structs by their underlying type. Note that this will 261 | /// only be successful if the underyling type is a struct or a map. 262 | #[inline] 263 | fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<()> 264 | where 265 | T: ser::Serialize, 266 | { 267 | value.serialize(self) 268 | } 269 | 270 | /// Serialize maps as `Tag_Compound` data. 271 | #[inline] 272 | fn serialize_map(self, _len: Option) -> Result { 273 | let header = self.header; // Circumvent strange borrowing errors. 274 | self.write_header(0x0a, header)?; 275 | Ok(Compound::from_outer(self)) 276 | } 277 | 278 | /// Serialize structs as `Tag_Compound` data. 279 | #[inline] 280 | fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { 281 | let header = self.header; // Circumvent strange borrowing errors. 282 | self.write_header(0x0a, header)?; 283 | Ok(Compound::from_outer(self)) 284 | } 285 | } 286 | 287 | impl<'a, 'b, W> serde::Serializer for &'a mut InnerEncoder<'a, 'b, W> 288 | where 289 | W: io::Write, 290 | { 291 | type Ok = (); 292 | type Error = Error; 293 | type SerializeSeq = Compound<'a, 'b, W>; 294 | type SerializeTuple = ser::Impossible<(), Error>; 295 | type SerializeTupleStruct = Compound<'a, 'b, W>; 296 | type SerializeTupleVariant = ser::Impossible<(), Error>; 297 | type SerializeMap = Compound<'a, 'b, W>; 298 | type SerializeStruct = Compound<'a, 'b, W>; 299 | type SerializeStructVariant = ser::Impossible<(), Error>; 300 | 301 | unrepresentable!( 302 | u8 u16 u32 u64 char unit newtype_variant tuple 303 | tuple_variant struct_variant 304 | ); 305 | 306 | #[inline] 307 | fn serialize_bool(self, value: bool) -> Result<()> { 308 | self.serialize_i8(value as i8) 309 | } 310 | 311 | #[inline] 312 | fn serialize_i8(self, value: i8) -> Result<()> { 313 | raw::write_bare_byte(&mut self.outer.writer, value).map_err(From::from) 314 | } 315 | 316 | #[inline] 317 | fn serialize_i16(self, value: i16) -> Result<()> { 318 | raw::write_bare_short(&mut self.outer.writer, value).map_err(From::from) 319 | } 320 | 321 | #[inline] 322 | fn serialize_i32(self, value: i32) -> Result<()> { 323 | raw::write_bare_int(&mut self.outer.writer, value).map_err(From::from) 324 | } 325 | 326 | #[inline] 327 | fn serialize_i64(self, value: i64) -> Result<()> { 328 | raw::write_bare_long(&mut self.outer.writer, value).map_err(From::from) 329 | } 330 | 331 | #[inline] 332 | fn serialize_f32(self, value: f32) -> Result<()> { 333 | raw::write_bare_float(&mut self.outer.writer, value).map_err(From::from) 334 | } 335 | 336 | #[inline] 337 | fn serialize_f64(self, value: f64) -> Result<()> { 338 | raw::write_bare_double(&mut self.outer.writer, value).map_err(From::from) 339 | } 340 | 341 | #[inline] 342 | fn serialize_str(self, value: &str) -> Result<()> { 343 | raw::write_bare_string(&mut self.outer.writer, value).map_err(From::from) 344 | } 345 | 346 | #[inline] 347 | fn serialize_unit_variant( 348 | self, 349 | _name: &'static str, 350 | _variant_index: u32, 351 | variant: &'static str, 352 | ) -> Result<()> { 353 | self.serialize_str(variant) 354 | } 355 | 356 | #[inline] 357 | fn serialize_bytes(self, _value: &[u8]) -> Result<()> { 358 | Err(Error::UnrepresentableType("u8")) 359 | } 360 | 361 | #[inline] 362 | fn serialize_none(self) -> Result<()> { 363 | Ok(()) 364 | } 365 | 366 | #[inline] 367 | fn serialize_some(self, value: &T) -> Result<()> 368 | where 369 | T: ser::Serialize, 370 | { 371 | value.serialize(self) 372 | } 373 | 374 | #[inline] 375 | fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { 376 | raw::close_nbt(&mut self.outer.writer).map_err(From::from) 377 | } 378 | 379 | #[inline] 380 | fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<()> 381 | where 382 | T: ser::Serialize, 383 | { 384 | value.serialize(self) 385 | } 386 | 387 | #[inline] 388 | fn serialize_seq(self, len: Option) -> Result { 389 | if let Some(l) = len { 390 | Compound::for_seq(self.outer, l as i32, false) 391 | } else { 392 | Err(Error::UnrepresentableType("unsized list")) 393 | } 394 | } 395 | 396 | #[inline] 397 | fn serialize_map(self, _len: Option) -> Result { 398 | Ok(Compound::from_outer(self.outer)) 399 | } 400 | 401 | #[inline] 402 | fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { 403 | Ok(Compound::from_outer(self.outer)) 404 | } 405 | 406 | fn serialize_tuple_struct( 407 | self, 408 | name: &'static str, 409 | len: usize, 410 | ) -> Result { 411 | match name { 412 | "__hematite_nbt_i8_array__" 413 | | "__hematite_nbt_i32_array__" 414 | | "__hematite_nbt_i64_array__" => Compound::for_seq(self.outer, len as i32, true), 415 | _ => Err(Error::UnrepresentableType(stringify!(tuple_struct))), 416 | } 417 | } 418 | } 419 | 420 | /// A serializer for valid map keys, i.e. strings. 421 | struct MapKeyEncoder<'a, 'b: 'a, W: 'a> { 422 | outer: &'a mut Encoder<'b, W>, 423 | } 424 | 425 | impl<'a, 'b: 'a, W: 'a> MapKeyEncoder<'a, 'b, W> 426 | where 427 | W: io::Write, 428 | { 429 | pub fn from_outer(outer: &'a mut Encoder<'b, W>) -> Self { 430 | MapKeyEncoder { outer } 431 | } 432 | } 433 | 434 | impl<'a, 'b: 'a, W: 'a> serde::Serializer for &'a mut MapKeyEncoder<'a, 'b, W> 435 | where 436 | W: io::Write, 437 | { 438 | type Ok = (); 439 | type Error = Error; 440 | type SerializeSeq = ser::Impossible<(), Error>; 441 | type SerializeTuple = ser::Impossible<(), Error>; 442 | type SerializeTupleStruct = ser::Impossible<(), Error>; 443 | type SerializeTupleVariant = ser::Impossible<(), Error>; 444 | type SerializeMap = ser::Impossible<(), Error>; 445 | type SerializeStruct = ser::Impossible<(), Error>; 446 | type SerializeStructVariant = ser::Impossible<(), Error>; 447 | 448 | return_expr_for_serialized_types!( 449 | Err(Error::NonStringMapKey); bool i8 i16 i32 i64 u8 u16 u32 u64 f32 f64 450 | char bytes unit unit_variant newtype_variant unit_struct seq tuple 451 | tuple_struct tuple_variant struct_variant newtype_struct map struct 452 | ); 453 | 454 | fn serialize_none(self) -> Result<()> { 455 | Ok(()) 456 | } 457 | 458 | fn serialize_some(self, value: &T) -> Result<()> 459 | where 460 | T: ser::Serialize, 461 | { 462 | value.serialize(self) 463 | } 464 | 465 | fn serialize_str(self, value: &str) -> Result<()> { 466 | raw::write_bare_string(&mut self.outer.writer, value) 467 | } 468 | } 469 | 470 | /// A serializer for valid map keys. 471 | struct TagEncoder<'a, 'b: 'a, W: 'a, K> { 472 | outer: &'a mut Encoder<'b, W>, 473 | key: Option, 474 | } 475 | 476 | impl<'a, 'b: 'a, W: 'a, K> TagEncoder<'a, 'b, W, K> 477 | where 478 | W: io::Write, 479 | K: serde::Serialize, 480 | { 481 | fn from_outer(outer: &'a mut Encoder<'b, W>, key: Option) -> Self { 482 | TagEncoder { outer, key } 483 | } 484 | 485 | fn write_header(&mut self, tag: i8) -> Result<()> { 486 | use serde::Serialize; 487 | raw::write_bare_byte(&mut self.outer.writer, tag)?; 488 | self.key 489 | .serialize(&mut MapKeyEncoder::from_outer(self.outer)) 490 | } 491 | } 492 | 493 | impl<'a, 'b: 'a, W: 'a, K> serde::Serializer for &'a mut TagEncoder<'a, 'b, W, K> 494 | where 495 | W: io::Write, 496 | K: serde::Serialize, 497 | { 498 | type Ok = (); 499 | type Error = Error; 500 | type SerializeSeq = NoOp; 501 | type SerializeTuple = ser::Impossible<(), Error>; 502 | type SerializeTupleStruct = NoOp; 503 | type SerializeTupleVariant = ser::Impossible<(), Error>; 504 | type SerializeMap = NoOp; 505 | type SerializeStruct = NoOp; 506 | type SerializeStructVariant = ser::Impossible<(), Error>; 507 | 508 | unrepresentable!( 509 | u8 u16 u32 u64 char unit newtype_variant tuple 510 | tuple_variant struct_variant 511 | ); 512 | 513 | #[inline] 514 | fn serialize_bool(self, value: bool) -> Result<()> { 515 | self.serialize_i8(value as i8) 516 | } 517 | 518 | #[inline] 519 | fn serialize_i8(self, _value: i8) -> Result<()> { 520 | self.write_header(0x01) 521 | } 522 | 523 | #[inline] 524 | fn serialize_i16(self, _value: i16) -> Result<()> { 525 | self.write_header(0x02) 526 | } 527 | 528 | #[inline] 529 | fn serialize_i32(self, _value: i32) -> Result<()> { 530 | self.write_header(0x03) 531 | } 532 | 533 | #[inline] 534 | fn serialize_i64(self, _value: i64) -> Result<()> { 535 | self.write_header(0x04) 536 | } 537 | 538 | #[inline] 539 | fn serialize_f32(self, _value: f32) -> Result<()> { 540 | self.write_header(0x05) 541 | } 542 | 543 | #[inline] 544 | fn serialize_f64(self, _value: f64) -> Result<()> { 545 | self.write_header(0x06) 546 | } 547 | 548 | #[inline] 549 | fn serialize_str(self, _value: &str) -> Result<()> { 550 | self.write_header(0x08) 551 | } 552 | 553 | #[inline] 554 | fn serialize_unit_variant( 555 | self, 556 | _name: &'static str, 557 | _variant_index: u32, 558 | variant: &'static str, 559 | ) -> Result<()> { 560 | self.serialize_str(variant) 561 | } 562 | 563 | #[inline] 564 | fn serialize_bytes(self, _value: &[u8]) -> Result<()> { 565 | Err(Error::UnrepresentableType("u8")) 566 | } 567 | 568 | #[inline] 569 | fn serialize_none(self) -> Result<()> { 570 | Ok(()) 571 | } 572 | 573 | #[inline] 574 | fn serialize_some(self, value: &T) -> Result<()> 575 | where 576 | T: ser::Serialize, 577 | { 578 | value.serialize(self) 579 | } 580 | 581 | #[inline] 582 | fn serialize_unit_struct(self, _name: &'static str) -> Result<()> { 583 | self.write_header(0x0a) 584 | } 585 | 586 | #[inline] 587 | fn serialize_newtype_struct(self, _name: &'static str, value: &T) -> Result<()> 588 | where 589 | T: ser::Serialize, 590 | { 591 | value.serialize(self) 592 | } 593 | 594 | #[inline] 595 | fn serialize_seq(self, len: Option) -> Result { 596 | if len.is_some() { 597 | self.write_header(0x09)?; 598 | Ok(NoOp) 599 | } else { 600 | Err(Error::UnrepresentableType("unsized list")) 601 | } 602 | } 603 | 604 | #[inline] 605 | fn serialize_map(self, _len: Option) -> Result { 606 | self.write_header(0x0a)?; 607 | Ok(NoOp) 608 | } 609 | 610 | #[inline] 611 | fn serialize_struct(self, _name: &'static str, _len: usize) -> Result { 612 | self.write_header(0x0a)?; 613 | Ok(NoOp) 614 | } 615 | 616 | fn serialize_tuple_struct( 617 | self, 618 | name: &'static str, 619 | _len: usize, 620 | ) -> Result { 621 | match name { 622 | "__hematite_nbt_i8_array__" => self.write_header(0x07)?, 623 | "__hematite_nbt_i32_array__" => self.write_header(0x0b)?, 624 | "__hematite_nbt_i64_array__" => self.write_header(0x0c)?, 625 | _ => return Err(Error::UnrepresentableType("tuple struct")), 626 | } 627 | 628 | Ok(NoOp) 629 | } 630 | } 631 | 632 | /// This empty serializer provides a way to serialize only headers/tags for 633 | /// sequences, maps, and structs. 634 | struct NoOp; 635 | 636 | impl ser::SerializeSeq for NoOp { 637 | type Ok = (); 638 | type Error = Error; 639 | 640 | fn serialize_element(&mut self, _value: &T) -> Result<()> 641 | where 642 | T: serde::Serialize, 643 | { 644 | Ok(()) 645 | } 646 | 647 | fn end(self) -> Result<()> { 648 | Ok(()) 649 | } 650 | } 651 | 652 | impl ser::SerializeTupleStruct for NoOp { 653 | type Ok = (); 654 | type Error = Error; 655 | 656 | fn serialize_field(&mut self, _value: &T) -> Result<()> 657 | where 658 | T: serde::Serialize, 659 | { 660 | Ok(()) 661 | } 662 | 663 | fn end(self) -> Result<()> { 664 | Ok(()) 665 | } 666 | } 667 | 668 | impl ser::SerializeStruct for NoOp { 669 | type Ok = (); 670 | type Error = Error; 671 | 672 | fn serialize_field(&mut self, _key: &'static str, _value: &T) -> Result<()> 673 | where 674 | T: serde::Serialize, 675 | { 676 | Ok(()) 677 | } 678 | 679 | fn end(self) -> Result<()> { 680 | Ok(()) 681 | } 682 | } 683 | 684 | impl ser::SerializeMap for NoOp { 685 | type Ok = (); 686 | type Error = Error; 687 | 688 | fn serialize_key(&mut self, _key: &T) -> Result<()> 689 | where 690 | T: serde::Serialize, 691 | { 692 | Ok(()) 693 | } 694 | 695 | fn serialize_value(&mut self, _value: &T) -> Result<()> 696 | where 697 | T: serde::Serialize, 698 | { 699 | Ok(()) 700 | } 701 | 702 | fn end(self) -> Result<()> { 703 | Ok(()) 704 | } 705 | } 706 | 707 | /// This function provides serde serialization support for NBT type `ByteArray`. 708 | /// 709 | /// It should be used in conjunction with serde's field annotation `serialize_with`. 710 | /// In the following example `byte_data` will be serialized as a `ByteArray` 711 | /// instead of a `List` of `Byte`s: 712 | /// 713 | /// ``` 714 | /// extern crate serde; 715 | /// use nbt::to_writer; 716 | /// use serde::Serialize; 717 | /// 718 | /// let mut serialized = Vec::new(); 719 | /// 720 | /// // Declare your struct 721 | /// #[derive(Serialize)] 722 | /// struct Sheep { 723 | /// #[serde(serialize_with="nbt::i8_array")] 724 | /// byte_data: Vec, 725 | /// } 726 | /// 727 | /// // Serialize to NBT! 728 | /// to_writer( 729 | /// &mut serialized, 730 | /// &Sheep { 731 | /// byte_data: vec![0x62, 0x69, 0x6E, 0x61, 0x72, 0x79, 0x20, 0x73, 0x68, 0x65, 0x65, 0x70], 732 | /// }, 733 | /// None 734 | /// ).unwrap(); 735 | /// 736 | /// print!("Serialized: {:?}", serialized); 737 | /// ``` 738 | pub fn i8_array(array: T, serializer: S) -> std::result::Result 739 | where 740 | T: IntoIterator, 741 | ::Item: std::borrow::Borrow, 742 | S: serde::ser::Serializer, 743 | { 744 | array_serializer!("i8_array", array, serializer) 745 | } 746 | 747 | /// This function provides serde serialization support for NBT type `IntArray`. 748 | /// 749 | /// It should be used in conjunction with serde's field annotation `serialize_with`. 750 | /// In the following example `int_data` will be serialized as an `IntArray` 751 | /// instead of a `List` of `Int`s: 752 | /// 753 | /// ``` 754 | /// extern crate serde; 755 | /// use nbt::to_writer; 756 | /// use serde::Serialize; 757 | /// 758 | /// let mut serialized = Vec::new(); 759 | /// 760 | /// // Declare your struct 761 | /// #[derive(Serialize)] 762 | /// struct Cow { 763 | /// #[serde(serialize_with="nbt::i32_array")] 764 | /// int_data: Vec, 765 | /// } 766 | /// 767 | /// // Serialize to NBT! 768 | /// to_writer( 769 | /// &mut serialized, 770 | /// &Cow { 771 | /// int_data: vec![1, 8, 64, 512, 4096, 32768, 262144], 772 | /// }, 773 | /// None 774 | /// ).unwrap(); 775 | /// 776 | /// print!("Serialized: {:?}", serialized); 777 | /// ``` 778 | pub fn i32_array(array: T, serializer: S) -> std::result::Result 779 | where 780 | T: IntoIterator, 781 | ::Item: std::borrow::Borrow, 782 | S: serde::ser::Serializer, 783 | { 784 | array_serializer!("i32_array", array, serializer) 785 | } 786 | 787 | /// This function provides serde serialization support for NBT type `LongArray`. 788 | /// 789 | /// It should be used in conjunction with serde's field annotation `serialize_with`. 790 | /// In the following example `int_data` will be serialized as a `LongArray` 791 | /// instead of a `List` of `Int`s: 792 | /// 793 | /// ``` 794 | /// extern crate serde; 795 | /// use nbt::to_writer; 796 | /// use serde::Serialize; 797 | /// 798 | /// let mut serialized = Vec::new(); 799 | /// 800 | /// // Declare your struct 801 | /// #[derive(Serialize)] 802 | /// struct Enderman { 803 | /// #[serde(serialize_with="nbt::i64_array")] 804 | /// long_data: Vec, 805 | /// } 806 | /// 807 | /// // Serialize to NBT! 808 | /// to_writer( 809 | /// &mut serialized, 810 | /// &Enderman { 811 | /// long_data: vec![0x1848ccd2157df10e, 0x64c5efff28280e9a], 812 | /// }, 813 | /// None 814 | /// ).unwrap(); 815 | /// 816 | /// print!("Serialized: {:?}", serialized); 817 | /// ``` 818 | pub fn i64_array(array: T, serializer: S) -> std::result::Result 819 | where 820 | T: IntoIterator, 821 | ::Item: std::borrow::Borrow, 822 | S: serde::ser::Serializer, 823 | { 824 | array_serializer!("i64_array", array, serializer) 825 | } 826 | --------------------------------------------------------------------------------