1use crate::tags::{Tag, TagIdent};
2use crate::error::{NBTResult, NBTError, digest_io};
3
4use byteorder::{BigEndian as BE, WriteBytesExt};
5use std::io::Write;
6use std::collections::HashMap;
7
8
9pub(crate) fn write_tag<W: Write>(writer: &mut W, tag: &Tag) -> NBTResult<()> {
10 match tag {
11 Tag::Byte(byte) => digest_io(writer.write_i8(*byte)),
13
14 Tag::Short(short) => digest_io(writer.write_i16::<BE>(*short)),
16
17 Tag::Int(int) => digest_io(writer.write_i32::<BE>(*int)),
19
20 Tag::Long(long) => digest_io(writer.write_i64::<BE>(*long)),
22
23 Tag::Float(float) => digest_io(writer.write_f32::<BE>(*float)),
25
26 Tag::Double(double) => digest_io(writer.write_f64::<BE>(*double)),
28
29 Tag::ByteArray(bytes) => {
31 digest_io(writer.write_u32::<BE>(bytes.len() as u32))?;
33
34 for byte in bytes {
36 digest_io(writer.write_i8(*byte))?;
37 }
38 Ok(())
39 }
40
41 Tag::String(string) => write_string(writer, &string),
43
44 Tag::List(list) => {
45 let list_type = ensure_list_integrity(&list)?;
47
48 digest_io(writer.write_u8(list_type as u8))?;
50
51 digest_io(writer.write_u32::<BE>(list.len() as u32))?;
53
54 for item in list {
56 write_tag(writer, &item)?;
57 }
58
59 Ok(())
60 }
61 Tag::Compound(compound) => write_compound(writer, compound),
62 Tag::IntArray(array) => {
63 digest_io(writer.write_u32::<BE>(array.len() as u32))?;
65
66 for int in array {
68 digest_io(writer.write_i32::<BE>(*int))?;
69 }
70 Ok(())
71 }
72 Tag::LongArray(array) => {
73 digest_io(writer.write_u32::<BE>(array.len() as u32))?;
75
76 for long in array {
78 digest_io(writer.write_i64::<BE>(*long))?;
79 }
80 Ok(())
81 }
82 }
83}
84
85
86pub(crate) fn ensure_list_integrity(list: &Vec<Tag>) -> NBTResult<TagIdent> {
88 if list.len() == 0 {
90 return Ok(TagIdent::TAG_End);
91 }
92
93 let tag = list.get(0).unwrap().ident();
97
98 for item in list {
100 if item.ident() != tag {
102 return Err(NBTError::InvalidList { found: item.ident(), expecting: tag })
104 }
105 }
106
107 Ok(tag)
108}
109
110pub(crate) fn write_string<W: Write>(writer: &mut W, string: &str) -> NBTResult<()> {
113 let bytes = encode_wonky_string(string);
115
116 digest_io(writer.write_u16::<BE>(bytes.len() as u16))?;
118
119 digest_io(writer.write_all(&bytes))
121}
122
123pub(crate) fn write_root<W: Write>(writer: &mut W, name: &str, elements: &HashMap<String, Tag>) -> NBTResult<()> {
125 digest_io(writer.write_u8(TagIdent::TAG_Compound as u8))?;
127
128 write_string(writer, &name)?;
130
131 write_compound(writer, elements)
133}
134
135pub(crate) fn write_compound<W: Write>(writer: &mut W, compound: &HashMap<String, Tag>) -> NBTResult<()> {
136 for (name, payload) in compound {
138 digest_io(writer.write_u8(payload.ident() as u8))?;
140
141 write_string(writer, &name)?;
143
144 write_tag(writer, payload)?;
146 }
147 digest_io(writer.write_u8(TagIdent::TAG_End as u8))
148}
149
150pub (crate) fn encode_wonky_string(s: &str) -> Vec<u8> {
151 cesu8::to_java_cesu8(&s).to_vec()
152}