1use crate::{CompoundTag, Tag};
2use byteorder::{BigEndian, WriteBytesExt};
3use flate2::write::{GzEncoder, ZlibEncoder};
4use std::io::{Error, Write};
5
6pub 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
17pub 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
28pub fn write_compound_tag<W: Write>(
51 writer: &mut W,
52 compound_tag: &CompoundTag,
53) -> Result<(), Error> {
54 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 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 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}