vintage_schematics/entity/
mod.rs1use color_eyre::{
5 eyre,
6 eyre::{Context, OptionExt, bail, eyre},
7};
8use itertools::Itertools;
9use serde::{Deserialize, Deserializer, Serialize, Serializer, de::Error};
10use serde_repr::{Deserialize_repr, Serialize_repr};
11
12use crate::{Map, ascii85, formats::internal::EntityMap};
13
14#[cfg(feature = "miniserde")]
15mod miniserde;
16
17const MAX_PREALLOC: usize = 1024;
23
24pub type BlockEntityMap = Map<u32, EntityMap>;
26
27#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
31pub enum Value {
32 Integer(i32),
34 Long(i64),
35 Double(f64),
36 Float(f32),
37 String(String),
38 Tree(Map<String, Self>),
39 ItemStack(Option<ItemStack>),
40 ByteArray(Vec<u8>),
41 Boolean(bool),
42 StringArray(Vec<String>),
43 IntArray(Vec<i32>),
44 FloatArray(Vec<f32>),
45 DoubleArray(Vec<f64>),
46 TreeArray(Vec<Map<String, Self>>),
47 LongArray(Vec<i64>),
48 BoolArray(Vec<bool>),
49}
50
51#[derive(Debug, Clone, PartialEq, Deserialize, Serialize)]
53pub struct ItemStack {
54 pub class: ItemClass,
56
57 pub id: i32,
59
60 pub stack_size: i32,
62
63 pub stack_attributes: EntityMap,
65}
66
67impl ItemStack {
68 fn encode(&self) -> Vec<u8> {
70 let mut output = Vec::with_capacity(16);
71 output.push(0);
72 output.extend((self.class as i32).to_le_bytes());
73 output.extend(self.id.to_le_bytes());
74 output.extend(self.stack_size.to_le_bytes());
75 output.extend(encode_entities(&self.stack_attributes));
76
77 output
78 }
79}
80
81#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize_repr, Serialize_repr)]
83#[repr(i32)]
84pub enum ItemClass {
85 Block = 0,
87
88 Item = 1,
91}
92
93impl TryFrom<i32> for ItemClass {
94 type Error = eyre::Error;
95
96 fn try_from(value: i32) -> Result<Self, Self::Error> {
97 match value {
98 0 => Ok(Self::Block),
99 1 => Ok(Self::Item),
100 _ => Err(eyre!("unknown item class: {value}")),
101 }
102 }
103}
104
105impl Value {
106 #[must_use]
117 pub const fn expected_length(&self) -> Option<usize> {
118 match self {
119 Self::Integer(_) | Self::Float(_) => Some(4),
120 Self::Long(_) | Self::Double(_) => Some(8),
121 Self::Boolean(_) => Some(1),
122 _ => None,
123 }
124 }
125
126 #[must_use]
128 pub const fn tag_byte(&self) -> u8 {
129 match self {
130 Self::Integer(_) => 1,
131 Self::Long(_) => 2,
132 Self::Double(_) => 3,
133 Self::Float(_) => 4,
134 Self::String(_) => 5,
135 Self::Tree(_) => 6,
136 Self::ItemStack(_) => 7,
137 Self::ByteArray(_) => 8,
138 Self::Boolean(_) => 9,
139 Self::StringArray(_) => 10,
140 Self::IntArray(_) => 11,
141 Self::FloatArray(_) => 12,
142 Self::DoubleArray(_) => 13,
143 Self::TreeArray(_) => 14,
144 Self::LongArray(_) => 15,
145 Self::BoolArray(_) => 16,
146 }
147 }
148
149 pub fn encode(&self) -> Vec<u8> {
156 match self {
157 Self::Integer(i) => i.to_le_bytes().to_vec(),
158 Self::Long(l) => l.to_le_bytes().to_vec(),
159 Self::Double(d) => d.to_le_bytes().to_vec(),
160 Self::Float(f) => f.to_le_bytes().to_vec(),
161 Self::String(s) => {
162 let mut output = encode_seven_bit_int(i32::try_from(s.len()).expect("string is too long"));
163 output.extend(s.as_bytes());
164 output
165 }
166 Self::Tree(t) => encode_entities(t),
167 Self::ByteArray(b) => {
168 let length = u16::try_from(b.len()).expect("byte arrays should contain less than 65,535 bytes");
169 let mut output = Vec::with_capacity(MAX_PREALLOC.min(b.len() + 2));
170 output.extend(length.to_le_bytes().iter());
171 output.extend(b);
172 output
173 }
174 Self::ItemStack(i) => i.as_ref().map_or_else(|| vec![1], ItemStack::encode),
175 Self::Boolean(b) => vec![u8::from(*b)],
176 Self::StringArray(s) => {
177 let mut output = Vec::with_capacity(MAX_PREALLOC.min(s.iter().map(|s| s.len() + 1).sum::<usize>() + 1));
178
179 #[allow(clippy::cast_possible_wrap, clippy::cast_possible_truncation)]
180 output.extend(encode_seven_bit_int(s.len() as i32));
181
182 for s in s {
183 output.extend(Self::String(s.clone()).encode());
184 }
185
186 output
187 }
188 Self::IntArray(i) => encode_array(i),
189 Self::FloatArray(f) => encode_array(f),
190 Self::DoubleArray(d) => encode_array(d),
191 Self::TreeArray(_t) => unimplemented!("tree arrays are unsupported"),
192 Self::LongArray(l) => encode_array(l),
193 Self::BoolArray(b) => {
194 let bytes = b.iter().map(|b| u8::from(*b)).collect::<Box<_>>();
195 encode_array(&bytes)
196 }
197 }
198 }
199
200 const fn is_string(&self) -> bool { matches!(self, Self::String(_)) }
202
203 const fn is_tree(&self) -> bool { matches!(self, Self::Tree(_)) }
205
206 const fn is_string_array(&self) -> bool { matches!(self, Self::StringArray(_)) }
208
209 const fn is_item_stack(&self) -> bool { matches!(self, Self::ItemStack(_)) }
211
212 const fn is_byte_array(&self) -> bool { matches!(self, Self::ByteArray(_)) }
214}
215
216impl TryFrom<u8> for Value {
217 type Error = eyre::Error;
218
219 fn try_from(value: u8) -> Result<Self, Self::Error> {
220 match value {
221 1 => Ok(Self::Integer(0)),
222 2 => Ok(Self::Long(0)),
223 3 => Ok(Self::Double(0.0)),
224 4 => Ok(Self::Float(0.0)),
225 5 => Ok(Self::String(String::new())),
226 6 => Ok(Self::Tree(Map::new())),
227 7 => Ok(Self::ItemStack(None)),
228 8 => Ok(Self::ByteArray(Vec::new())),
229 9 => Ok(Self::Boolean(false)),
230 10 => Ok(Self::StringArray(Vec::new())),
231 11 => Ok(Self::IntArray(Vec::new())),
232 12 => Ok(Self::FloatArray(Vec::new())),
233 13 => Ok(Self::DoubleArray(Vec::new())),
234 14 => Ok(Self::TreeArray(Vec::new())),
235 15 => Ok(Self::LongArray(Vec::new())),
236 16 => Ok(Self::BoolArray(Vec::new())),
237 _ => Err(eyre!("unknown kind (not in range 1..=16): {value}")),
238 }
239 }
240}
241
242impl Default for Value {
243 fn default() -> Self { Self::Integer(0) }
244}
245
246#[derive(Debug, Clone, Default)]
247pub struct BlockEntities(pub BlockEntityMap);
249
250impl<'de> Deserialize<'de> for BlockEntities {
251 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
252 where
253 D: Deserializer<'de>,
254 {
255 let map: Map<u32, String> = Deserialize::deserialize(deserializer)?;
256 Ok(Self(
257 map
258 .into_iter()
259 .map(|(id, value)| parse_encoded_entities(&value).map(|v| (id, v)))
260 .collect::<Result<_, _>>()
261 .map_err(D::Error::custom)?,
262 ))
263 }
264}
265
266impl Serialize for BlockEntities {
267 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
268 where
269 S: Serializer,
270 {
271 let map: Map<u32, String> = self
272 .0
273 .iter()
274 .map(|(&id, entities)| (id, ascii85::encode(&encode_entities(entities), false)))
275 .collect();
276 map.serialize(serializer)
277 }
278}
279
280pub fn parse_encoded_entities(input: &str) -> eyre::Result<EntityMap> {
287 let mut input = ascii85::decode(input)?.into_iter();
288 let input = &mut input;
289
290 parse_entities(input)
291}
292
293pub fn parse_entities(input: &mut impl Iterator<Item = u8>) -> eyre::Result<EntityMap> {
299 let mut map = Map::new();
300
301 while let Some(&byte) = input.next().as_ref() {
308 if byte == 0 {
311 break;
312 }
313
314 let name = parse_string(input)?;
316
317 let mut value = Value::try_from(byte)?;
319
320 value = if value.is_tree() {
321 Value::Tree(parse_entities(input)?)
322 } else if value.is_string_array() {
323 let count = i32::from_le_bytes(input.next_array().ok_or_eyre("EOF while parsing array length")?);
324
325 #[allow(clippy::cast_sign_loss)]
326 let mut strings = Vec::with_capacity(MAX_PREALLOC.min(count as usize));
327 for _ in 0..count {
328 strings.push(parse_string(input)?);
329 }
330
331 Value::StringArray(strings)
332 } else if value.is_item_stack() {
333 Value::ItemStack(parse_item_stack(input)?)
334 } else if value.is_byte_array() {
335 let length = u16::from_le_bytes(input.next_array().ok_or_eyre("byte array length should be two bytes")?) as usize;
336 Value::ByteArray(input.take(length).collect())
337 } else {
338 let value_length = value
339 .expected_length()
340 .map_or_else(
341 || {
342 if value.is_string() {
343 usize::try_from(parse_seven_bit_int(input)?)
344 } else {
345 usize::try_from(i32::from_le_bytes(input.next_array().ok_or_eyre("EOF while parsing array length")?))
346 }
347 .context("invalid value for string/array length")
348 },
349 Ok,
350 )
351 .context("lengths should be positive")?;
352
353 let data: Vec<u8> = input.take(value_length).collect();
354 match value {
355 Value::Integer(_) => {
358 Value::Integer(i32::from_le_bytes(data.try_into().map_err(|_| eyre!("EOF while parsing integer"))?))
359 }
360 Value::Long(_) => {
361 Value::Long(i64::from_le_bytes(data.try_into().map_err(|_| eyre!("EOF while parsing long"))?))
362 }
363 Value::Double(_) => {
364 Value::Double(f64::from_le_bytes(data.try_into().map_err(|_| eyre!("EOF while parsing double"))?))
365 }
366 Value::Float(_) => {
367 Value::Float(f32::from_le_bytes(data.try_into().map_err(|_| eyre!("EOF while parsing float"))?))
368 }
369 Value::Boolean(_) => Value::Boolean(*data.first().ok_or_eyre("EOF while parsing bool")? != 0),
370 Value::String(_) => Value::String(String::from_utf8(data).context("invalid string value")?),
371 Value::IntArray(_) => Value::IntArray(parse_array(&mut data.into_iter(), value_length)?),
372 Value::FloatArray(_) => Value::FloatArray(parse_array(&mut data.into_iter(), value_length)?),
373 Value::DoubleArray(_) => Value::DoubleArray(parse_array(&mut data.into_iter(), value_length)?),
374 Value::TreeArray(_) => bail!("tree arrays are unsupported"),
375 Value::LongArray(_) => Value::LongArray(parse_array(&mut data.into_iter(), value_length)?),
376 Value::BoolArray(_) => {
377 let array = parse_array::<u8>(&mut data.into_iter(), value_length)?;
378 Value::BoolArray(array.iter().map(|b| *b != 0).collect::<Vec<bool>>())
379 }
380
381 Value::Tree(_) | Value::StringArray(_) | Value::ItemStack(_) | Value::ByteArray(_) => {
382 unreachable!("should have been parsed already")
383 }
384 }
385 };
386
387 map.insert(name, value);
388 }
389
390 Ok(map)
391}
392
393pub fn parse_array<T>(input: &mut dyn Iterator<Item = u8>, length: usize) -> eyre::Result<Vec<T>>
400where
401 T: num_traits::FromBytes,
402 for<'a> &'a [u8]: TryInto<&'a T::Bytes>,
403{
404 let mut output = Vec::with_capacity(MAX_PREALLOC.min(length));
405 for _ in 0..length {
406 let bytes = input.take(size_of::<T>()).collect::<Vec<u8>>();
407 let bytes = bytes
408 .chunks_exact(size_of::<T>())
409 .next()
410 .ok_or_else(|| eyre!("unexpected EOF while parsing {}", std::any::type_name::<T>()))?;
411 let bytes =
412 TryInto::<&T::Bytes>::try_into(bytes).map_err(|_| eyre!("couldn't convert slice to num_traits::Bytes"))?;
413 output.push(T::from_le_bytes(bytes));
414 }
415
416 Ok(output)
417}
418
419pub fn encode_array<T>(array: &[T]) -> Vec<u8>
426where
427 T: num_traits::ToBytes,
428{
429 let mut output = Vec::with_capacity(size_of_val(array) + 4);
430
431 output.extend_from_slice(
432 &i32::try_from(array.len()).expect("arrays should contain less than i32::MAX items").to_le_bytes(),
433 );
434
435 for item in array {
436 output.extend_from_slice(item.to_le_bytes().as_ref());
437 }
438
439 output
440}
441
442pub fn parse_item_stack(input: &mut impl Iterator<Item = u8>) -> eyre::Result<Option<ItemStack>> {
448 if input.next().ok_or_eyre("EOF while parsing item stack")? == 1 {
449 Ok(None)
451 } else {
452 Ok(Some(ItemStack {
453 class: ItemClass::try_from(i32::from_le_bytes(input.next_array().ok_or_eyre("EOF while parsing item class")?))?,
454 id: i32::from_le_bytes(input.next_array().ok_or_eyre("EOF while parsing item id")?),
455 stack_size: i32::from_le_bytes(input.next_array().ok_or_eyre("EOF while parsing item stack size")?),
456 stack_attributes: parse_entities(input)?,
457 }))
458 }
459}
460
461pub fn parse_string(input: &mut dyn Iterator<Item = u8>) -> eyre::Result<String> {
468 let length = usize::try_from(parse_seven_bit_int(input)?).context("string length should be positive")?;
469 if length == 0 {
470 bail!("string length should be non-zero")
471 }
472 String::from_utf8(input.take(length).collect()).context("invalid string")
473}
474
475pub fn parse_seven_bit_int(input: &mut dyn Iterator<Item = u8>) -> eyre::Result<i32> {
482 let shifts = (0..=u32::MAX).step_by(7);
492
493 input
494 .take_while_inclusive(|&byte| byte >= 0b1000_0000)
496 .zip(shifts)
497 .try_fold(0i32, |acc, (byte, shift)| {
498 i32::from(byte & 0b0111_1111) .checked_shl(shift)
500 .and_then(|value| acc.checked_add(value))
501 .ok_or_eyre("integer value too large: overflow")
502 })
503}
504
505#[must_use]
508pub fn encode_seven_bit_int(value: i32) -> Vec<u8> {
509 let mut output = Vec::with_capacity(4);
510
511 #[allow(clippy::cast_sign_loss)]
512 let mut value = value as u32;
513
514 let mask = 0b1000_0000;
516
517 while value >= mask {
518 #[allow(clippy::cast_possible_truncation)]
519 output.push((value | mask) as u8);
520 value >>= 7;
521 }
522
523 #[allow(clippy::cast_possible_truncation)]
525 output.push(value as u8);
526
527 output
528}
529
530#[must_use]
532pub fn encode_entities(entities: &EntityMap) -> Vec<u8> {
533 let mut output = Vec::new();
534 for (key, value) in entities {
535 output.push(value.tag_byte());
536 let key = Value::String(key.clone()).encode();
537 output.extend(key);
538 output.extend(value.encode());
539 }
540 output.push(0);
542 output
543}