aboutsummaryrefslogtreecommitdiff
path: root/azalea-nbt/src/encode.rs
blob: 9ce4faf4d2a405951649ecda0c9b9bb9580c9f60 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
use crate::Error;
use crate::Tag;
use byteorder::{WriteBytesExt, BE};
use flate2::write::{GzEncoder, ZlibEncoder};
use std::io::Write;

#[inline]
fn write_string(writer: &mut dyn Write, string: &str) -> Result<(), Error> {
    writer
        .write_i16::<BE>(string.len() as i16)
        .map_err(|_| Error::WriteError)?;
    writer
        .write_all(string.as_bytes())
        .map_err(|_| Error::WriteError)?;

    Ok(())
}

impl Tag {
    pub fn write_without_end(&self, writer: &mut dyn Write) -> Result<(), Error> {
        match self {
            Tag::End => {}
            Tag::Byte(value) => writer.write_i8(*value).map_err(|_| Error::WriteError)?,
            Tag::Short(value) => writer
                .write_i16::<BE>(*value)
                .map_err(|_| Error::WriteError)?,
            Tag::Int(value) => writer
                .write_i32::<BE>(*value)
                .map_err(|_| Error::WriteError)?,
            Tag::Long(value) => writer
                .write_i64::<BE>(*value)
                .map_err(|_| Error::WriteError)?,
            Tag::Float(value) => writer
                .write_f32::<BE>(*value)
                .map_err(|_| Error::WriteError)?,
            Tag::Double(value) => writer
                .write_f64::<BE>(*value)
                .map_err(|_| Error::WriteError)?,
            Tag::ByteArray(value) => {
                writer
                    .write_i32::<BE>(value.len() as i32)
                    .map_err(|_| Error::WriteError)?;
                for &byte in value {
                    writer.write_i8(byte).map_err(|_| Error::WriteError)?;
                }
            }
            Tag::String(value) => {
                write_string(writer, value)?;
            }
            Tag::List(value) => {
                // we just get the type from the first item, or default the type to END
                if value.is_empty() {
                    writer.write_i8(0).map_err(|_| Error::WriteError)?;
                    writer.write_i32::<BE>(0).map_err(|_| Error::WriteError)?;
                } else {
                    let type_id = value[0].id();
                    writer.write_u8(type_id).map_err(|_| Error::WriteError)?;
                    writer
                        .write_i32::<BE>(value.len() as i32)
                        .map_err(|_| Error::WriteError)?;
                    for tag in value {
                        tag.write_without_end(writer)?;
                    }
                }
            }
            Tag::Compound(value) => {
                for (key, tag) in value {
                    writer.write_u8(tag.id()).map_err(|_| Error::WriteError)?;
                    write_string(writer, key)?;
                    tag.write_without_end(writer)?;
                }
                writer
                    .write_u8(Tag::End.id())
                    .map_err(|_| Error::WriteError)?;
            }
            Tag::IntArray(value) => {
                writer
                    .write_i32::<BE>(value.len() as i32)
                    .map_err(|_| Error::WriteError)?;
                for &int in value {
                    writer.write_i32::<BE>(int).map_err(|_| Error::WriteError)?;
                }
            }
            Tag::LongArray(value) => {
                writer
                    .write_i32::<BE>(value.len() as i32)
                    .map_err(|_| Error::WriteError)?;
                for &long in value {
                    writer
                        .write_i64::<BE>(long)
                        .map_err(|_| Error::WriteError)?;
                }
            }
        }

        Ok(())
    }

    pub fn write(&self, writer: &mut impl Write) -> Result<(), Error> {
        match self {
            Tag::Compound(value) => {
                for (key, tag) in value {
                    writer.write_u8(tag.id()).map_err(|_| Error::WriteError)?;
                    write_string(writer, key)?;
                    tag.write_without_end(writer)?;
                }
                Ok(())
            }
            _ => Err(Error::InvalidTag),
        }
    }

    pub fn write_zlib(&self, writer: &mut impl Write) -> Result<(), Error> {
        let mut encoder = ZlibEncoder::new(writer, flate2::Compression::default());
        self.write(&mut encoder)
    }

    pub fn write_gzip(&self, writer: &mut impl Write) -> Result<(), Error> {
        let mut encoder = GzEncoder::new(writer, flate2::Compression::default());
        self.write(&mut encoder)
    }
}