nbt/
encode.rs

1use crate::{CompoundTag, Tag};
2use byteorder::{BigEndian, WriteBytesExt};
3use flate2::write::{GzEncoder, ZlibEncoder};
4use std::io::{Error, Write};
5
6/// Write a compound tag to writer using gzip compression.
7pub fn write_gzip_compound_tag<W: Write>(
8    writer: &mut W,
9    compound_tag: &CompoundTag,
10) -> Result<(), Error> {
11    write_compound_tag(
12        &mut GzEncoder::new(writer, Default::default()),
13        compound_tag,
14    )
15}
16
17/// Write a compound tag to writer using zlib compression.
18pub fn write_zlib_compound_tag<W: Write>(
19    writer: &mut W,
20    compound_tag: &CompoundTag,
21) -> Result<(), Error> {
22    write_compound_tag(
23        &mut ZlibEncoder::new(writer, Default::default()),
24        compound_tag,
25    )
26}
27
28/// Write a compound tag to writer.
29///
30/// # Example
31/// ```
32/// use nbt::encode::write_compound_tag;
33/// use nbt::CompoundTag;
34///
35/// let mut server = CompoundTag::new();
36///
37/// server.insert_str("ip", "localhost:25565");
38/// server.insert_str("name", "Minecraft Server");
39/// server.insert_bool("hideAddress", true);
40///
41/// let mut servers = Vec::new();
42/// servers.push(server);
43///
44/// let mut root_tag = CompoundTag::new();
45/// root_tag.insert_compound_tag_vec("servers", servers);
46///
47/// let mut vec = Vec::new();
48/// write_compound_tag(&mut vec, &root_tag).unwrap();
49/// ```
50pub fn write_compound_tag<W: Write>(
51    writer: &mut W,
52    compound_tag: &CompoundTag,
53) -> Result<(), Error> {
54    // Tag id
55    writer.write_u8(Tag::Compound(CompoundTag::new()).type_id())?;
56    
57    write_string(writer, compound_tag.name.as_deref().unwrap_or(""))?;
58
59    write_inner_compound_tag(writer, compound_tag)
60}
61
62pub fn write_inner_compound_tag<W: Write>(
63    writer: &mut W,
64    compound_tag: &CompoundTag,
65) -> Result<(), Error> {
66    for (name, tag) in &compound_tag.tags {
67        writer.write_u8(tag.type_id())?;
68        write_string(writer, name)?;
69        write_tag(writer, tag)?;
70    }
71
72    // To mark compound tag end.
73    writer.write_u8(0)
74}
75
76fn write_tag<W: Write>(writer: &mut W, tag: &Tag) -> Result<(), Error> {
77    match tag {
78        Tag::Byte(value) => writer.write_i8(*value)?,
79        Tag::Short(value) => writer.write_i16::<BigEndian>(*value)?,
80        Tag::Int(value) => writer.write_i32::<BigEndian>(*value)?,
81        Tag::Long(value) => writer.write_i64::<BigEndian>(*value)?,
82        Tag::Float(value) => writer.write_f32::<BigEndian>(*value)?,
83        Tag::Double(value) => writer.write_f64::<BigEndian>(*value)?,
84        Tag::ByteArray(value) => {
85            writer.write_u32::<BigEndian>(value.len() as u32)?;
86
87            for v in value {
88                writer.write_i8(*v)?;
89            }
90        }
91        Tag::String(value) => write_string(writer, value)?,
92        Tag::List(value) => {
93            if value.len() > 0 {
94                writer.write_u8(value[0].type_id())?;
95            } else {
96                // Empty list type.
97                writer.write_u8(0)?;
98            }
99
100            writer.write_u32::<BigEndian>(value.len() as u32)?;
101
102            for tag in value {
103                write_tag(writer, tag)?;
104            }
105        }
106        Tag::Compound(value) => write_inner_compound_tag(writer, value)?,
107        Tag::IntArray(value) => {
108            writer.write_u32::<BigEndian>(value.len() as u32)?;
109
110            for v in value {
111                writer.write_i32::<BigEndian>(*v)?;
112            }
113        }
114        Tag::LongArray(value) => {
115            writer.write_u32::<BigEndian>(value.len() as u32)?;
116
117            for v in value {
118                writer.write_i64::<BigEndian>(*v)?;
119            }
120        }
121    }
122
123    Ok(())
124}
125
126fn write_string<W: Write>(writer: &mut W, value: &str) -> Result<(), Error> {
127    writer.write_u16::<BigEndian>(value.len() as u16)?;
128    writer.write(value.as_bytes())?;
129
130    Ok(())
131}
132
133#[test]
134fn test_hello_world_write() {
135    let mut hello_world = CompoundTag::named("hello world");
136    hello_world.insert_str("name", "Bananrama");
137
138    let mut vec = Vec::new();
139    write_compound_tag(&mut vec, &hello_world).unwrap();
140
141    assert_eq!(
142        vec,
143        include_bytes!("../test/binary/hello_world.dat").to_vec()
144    );
145}
146
147#[test]
148fn test_servers_write() {
149    let mut server = CompoundTag::new();
150
151    server.insert_str("ip", "localhost:25565");
152    server.insert_str("name", "Minecraft Server");
153    server.insert_bool("hideAddress", true);
154
155    let mut servers = Vec::new();
156    servers.push(server);
157
158    let mut root_tag = CompoundTag::new();
159    root_tag.insert_compound_tag_vec("servers", servers);
160
161    let mut vec = Vec::new();
162    write_compound_tag(&mut vec, &root_tag).unwrap();
163
164    assert_eq!(vec, include_bytes!("../test/binary/servers.dat").to_vec());
165}