1#[macro_use]
2extern crate combine;
3
4extern crate libflate;
5
6extern crate failure;
7
8use combine::parser::byte::{
9 byte,
10 num::{be_f32, be_f64, be_i16, be_i32, be_i64, be_u16},
11};
12use combine::stream::{buffered::BufferedStream, state::State, ReadStream};
13use combine::{any, count, many, unexpected};
14use combine::{ParseError, Parser, Stream};
15
16use std::{io::Read, mem};
17
18#[derive(Clone, Debug, PartialEq)]
20pub enum UnnamedTag {
21 End,
23
24 Byte(i8),
26
27 Short(i16),
29
30 Int(i32),
32
33 Long(i64),
35
36 Float(f32),
38
39 Double(f64),
41
42 ByteArray(Vec<i8>),
44
45 String(String),
47
48 List(Vec<UnnamedTag>),
51
52 Compound(Vec<NamedTag>),
55}
56
57#[derive(Clone, Debug, PartialEq)]
60pub struct NamedTag {
61 pub name: String,
63 pub content: UnnamedTag,
65}
66
67fn name<I>() -> impl Parser<Input = I, Output = String>
68where
69 I: Stream<Item = u8>,
70 I::Error: ParseError<I::Item, I::Range, I::Position>,
72{
73 be_u16()
74 .then(|length| count(length as usize, any()))
75 .map(|contents: Vec<u8>| String::from_utf8(contents).unwrap())
76}
77
78fn end_tag<I>() -> impl Parser<Input = I, Output = NamedTag>
79where
80 I: Stream<Item = u8>,
81 I::Error: ParseError<I::Item, I::Range, I::Position>,
83{
84 byte(0).map(|_| NamedTag {
85 name: String::new(),
86 content: UnnamedTag::End,
87 })
88}
89
90fn i8<I>() -> impl Parser<Input = I, Output = i8>
91where
92 I: Stream<Item = u8>,
93 I::Error: ParseError<I::Item, I::Range, I::Position>,
95{
96 any().map(|n| unsafe { mem::transmute::<u8, i8>(n) })
97}
98
99macro_rules! simple_number_tag {
100 ($func_name:ident, $parser_name:ident, $tag_variant:path) => {
101 fn $func_name<I>() -> impl Parser<Input = I, Output = UnnamedTag>
102 where
103 I: Stream<Item = u8>,
104 I::Error: ParseError<I::Item, I::Range, I::Position>,
106 {
107 $parser_name().map($tag_variant)
108 }
109 };
110}
111
112simple_number_tag!(byte_tag, i8, UnnamedTag::Byte);
113simple_number_tag!(short_tag, be_i16, UnnamedTag::Short);
114simple_number_tag!(int_tag, be_i32, UnnamedTag::Int);
115simple_number_tag!(long_tag, be_i64, UnnamedTag::Long);
116simple_number_tag!(float_tag, be_f32, UnnamedTag::Float);
117simple_number_tag!(double_tag, be_f64, UnnamedTag::Double);
118
119fn bytearray_tag<I>() -> impl Parser<Input = I, Output = UnnamedTag>
120where
121 I: Stream<Item = u8>,
122 I::Error: ParseError<I::Item, I::Range, I::Position>,
124{
125 be_i32()
126 .then(|length| count(length as usize, i8()))
127 .map(UnnamedTag::ByteArray)
128}
129
130fn string_tag<I>() -> impl Parser<Input = I, Output = UnnamedTag>
131where
132 I: Stream<Item = u8>,
133 I::Error: ParseError<I::Item, I::Range, I::Position>,
135{
136 name().map(UnnamedTag::String)
137}
138
139fn list_tag<I>() -> impl Parser<Input = I, Output = UnnamedTag>
140where
141 I: Stream<Item = u8>,
142 I::Error: ParseError<I::Item, I::Range, I::Position>,
144{
145 i8().and(be_i32())
146 .then(|(tag_id, length)| {
147 count(
148 length as usize,
149 combine::parser(move |input| match tag_id {
150 0 => end_tag()
151 .map(|NamedTag { content, .. }| content)
152 .parse_stream(input),
153 1 => byte_tag().parse_stream(input),
154 2 => short_tag().parse_stream(input),
155 3 => int_tag().parse_stream(input),
156 4 => long_tag().parse_stream(input),
157 5 => float_tag().parse_stream(input),
158 6 => double_tag().parse_stream(input),
159 7 => bytearray_tag().parse_stream(input),
160 8 => string_tag().parse_stream(input),
161 9 => list_tag().parse_stream(input),
162 10 => compound_tag().parse_stream(input),
163 _ => unexpected("Invalid tagId on TAG_List")
164 .map(|()| UnnamedTag::End)
165 .parse_stream(input),
166 }),
167 )
168 })
169 .map(UnnamedTag::List)
170}
171
172fn compound_tag<I>() -> impl Parser<Input = I, Output = UnnamedTag>
173where
174 I: Stream<Item = u8>,
175 I::Error: ParseError<I::Item, I::Range, I::Position>,
177{
178 (many(combine::parser(|input| {
179 let (tag, rest) = named_tag().parse_stream(input)?;
180 if tag.content == UnnamedTag::End {
181 combine::unexpected("If you see this, contact github.com/PurpleMyst")
182 .map(|_| tag.clone())
183 .parse_stream(input)
184 } else {
185 Ok((tag, rest))
186 }
187 }))).skip(end_tag())
188 .map(UnnamedTag::Compound)
189}
190
191fn named_tag<I>() -> impl Parser<Input = I, Output = NamedTag>
192where
193 I: Stream<Item = u8>,
194 I::Error: ParseError<I::Item, I::Range, I::Position>,
196{
197 macro_rules! do_it {
198 ($num:expr => $parser:expr) => {
199 byte($num)
200 .with(name())
201 .and($parser)
202 .map(|(name, content)| NamedTag { name, content })
203 };
204 }
205
206 choice!(
207 end_tag(),
208 do_it!(1 => byte_tag()),
209 do_it!(2 => short_tag()),
210 do_it!(3 => int_tag()),
211 do_it!(4 => long_tag()),
212 do_it!(5 => float_tag()),
213 do_it!(6 => double_tag()),
214 do_it!(7 => bytearray_tag()),
215 do_it!(8 => string_tag()),
216 do_it!(9 => list_tag()),
217 do_it!(10 => compound_tag())
218 )
219}
220
221pub fn decode<R: Read>(mut input: R) -> Result<NamedTag, failure::Error> {
224 let decoder = libflate::gzip::Decoder::new(&mut input)?;
225 let mut stream = BufferedStream::new(State::new(ReadStream::new(decoder)), 4096);
226 Ok(named_tag().parse_stream(&mut stream).map_err(|c| c.into_inner().error)?.0)
227}