use std::{
collections::BTreeMap,
io::{self, Read},
};
use crate::{
BinaryString, BrickColor, Color3, ColorSequence, ColorSequenceKeypoint, NumberRange,
NumberSequence, NumberSequenceKeypoint, Rect, UDim, UDim2, Variant, VariantType, Vector2,
Vector3,
};
use super::{type_id, AttributeError};
pub(crate) fn read_attributes<R: Read>(
mut value: R,
) -> Result<BTreeMap<String, Variant>, AttributeError> {
let mut attributes = BTreeMap::new();
let len = match read_option_u32(&mut value) {
Ok(Some(len)) => len,
Ok(None) => return Ok(attributes),
Err(_) => return Err(AttributeError::InvalidLength),
};
for _ in 0..len {
let key_buf = read_string(&mut value).map_err(|_| AttributeError::NoKey)?;
let key = String::from_utf8(key_buf).map_err(AttributeError::KeyBadUnicode)?;
let type_id = read_u8(&mut value).map_err(|_| AttributeError::NoValueType)?;
let ty =
type_id::to_variant_type(type_id).ok_or(AttributeError::InvalidValueType(type_id))?;
let value = match ty {
VariantType::BrickColor => {
let color =
read_u32(&mut value).map_err(|_| AttributeError::ReadType("BrickColor"))?;
BrickColor::from_number(color as u16)
.ok_or(AttributeError::InvalidBrickColor(color))?
.into()
}
VariantType::Bool => {
(read_u8(&mut value).map_err(|_| AttributeError::ReadType("bool"))? != 0).into()
}
VariantType::Color3 => read_color3(&mut value)
.map_err(|_| AttributeError::ReadType("Color3"))?
.into(),
VariantType::ColorSequence => {
let size = read_u32(&mut value)
.map_err(|_| AttributeError::ReadType("ColorSequence length"))?;
let mut keypoints = Vec::with_capacity(size as usize);
for _ in 0..size {
let _envelope = read_f32(&mut value)
.map_err(|_| AttributeError::ReadType("ColorSequenceKeypoint envelope"))?;
let time = read_f32(&mut value)
.map_err(|_| AttributeError::ReadType("ColorSequenceKeypoint time"))?;
let color = read_color3(&mut value)
.map_err(|_| AttributeError::ReadType("ColorSequenceKeypoint color"))?;
keypoints.push(ColorSequenceKeypoint::new(time, color));
}
ColorSequence { keypoints }.into()
}
VariantType::Float32 => read_f32(&mut value)
.map_err(|_| AttributeError::ReadType("float32"))?
.into(),
VariantType::Float64 => read_f64(&mut value)
.map_err(|_| AttributeError::ReadType("float64"))?
.into(),
VariantType::NumberRange => NumberRange::new(
read_f32(&mut value).map_err(|_| AttributeError::ReadType("NumberRange min"))?,
read_f32(&mut value).map_err(|_| AttributeError::ReadType("NumberRange max"))?,
)
.into(),
VariantType::NumberSequence => {
let size = read_u32(&mut value)
.map_err(|_| AttributeError::ReadType("NumberSequence length"))?;
let mut keypoints = Vec::with_capacity(size as usize);
for _ in 0..size {
let envelope = read_f32(&mut value)
.map_err(|_| AttributeError::ReadType("NumberSequence envelope"))?;
let time = read_f32(&mut value)
.map_err(|_| AttributeError::ReadType("NumberSequence time"))?;
let value = read_f32(&mut value)
.map_err(|_| AttributeError::ReadType("NumberSequence value"))?;
keypoints.push(NumberSequenceKeypoint::new(time, value, envelope));
}
NumberSequence { keypoints }.into()
}
VariantType::Rect => Rect::new(
read_vector2(&mut value).map_err(|_| AttributeError::ReadType("Rect min"))?,
read_vector2(&mut value).map_err(|_| AttributeError::ReadType("Rect max"))?,
)
.into(),
VariantType::BinaryString => {
let binary_string: BinaryString = read_string(&mut value)
.map_err(|_| AttributeError::ReadType("string"))?
.into();
binary_string.into()
}
VariantType::UDim => read_udim(&mut value)
.map_err(|_| AttributeError::ReadType("UDim"))?
.into(),
VariantType::UDim2 => UDim2::new(
read_udim(&mut value).map_err(|_| AttributeError::ReadType("UDim2 X"))?,
read_udim(&mut value).map_err(|_| AttributeError::ReadType("UDim2 Y"))?,
)
.into(),
VariantType::Vector2 => Vector2::new(
read_f32(&mut value).map_err(|_| AttributeError::ReadType("Vector2 X"))?,
read_f32(&mut value).map_err(|_| AttributeError::ReadType("Vector2 Y"))?,
)
.into(),
VariantType::Vector3 => Vector3::new(
read_f32(&mut value).map_err(|_| AttributeError::ReadType("Vector3 X"))?,
read_f32(&mut value).map_err(|_| AttributeError::ReadType("Vector3 Y"))?,
read_f32(&mut value).map_err(|_| AttributeError::ReadType("Vector3 Z"))?,
)
.into(),
other => return Err(AttributeError::UnsupportedVariantType(other)),
};
attributes.insert(key, value);
}
Ok(attributes)
}
fn read_u8<R: Read>(mut reader: R) -> io::Result<u8> {
let mut bytes = [0u8; 1];
reader.read_exact(&mut bytes)?;
Ok(bytes[0])
}
fn read_i32<R: Read>(mut reader: R) -> io::Result<i32> {
let mut bytes = [0u8; 4];
reader.read_exact(&mut bytes)?;
Ok(i32::from_le_bytes(bytes))
}
fn read_option_u32<R: Read>(reader: R) -> io::Result<Option<u32>> {
let mut bytes = [0u8; 4];
if read_exact_or_none(reader, &mut bytes)? {
Ok(Some(u32::from_le_bytes(bytes)))
} else {
Ok(None)
}
}
fn read_u32<R: Read>(mut reader: R) -> io::Result<u32> {
let mut bytes = [0u8; 4];
reader.read_exact(&mut bytes)?;
Ok(u32::from_le_bytes(bytes))
}
fn read_f32<R: Read>(mut reader: R) -> io::Result<f32> {
let mut bytes = [0u8; 4];
reader.read_exact(&mut bytes)?;
Ok(f32::from_le_bytes(bytes))
}
fn read_f64<R: Read>(mut reader: R) -> io::Result<f64> {
let mut bytes = [0u8; 8];
reader.read_exact(&mut bytes)?;
Ok(f64::from_le_bytes(bytes))
}
fn read_string<R: Read>(mut reader: R) -> io::Result<Vec<u8>> {
let size = read_u32(&mut reader)? as usize;
let mut characters = vec![0u8; size];
reader.read_exact(&mut characters)?;
Ok(characters)
}
fn read_color3<R: Read>(mut reader: R) -> io::Result<Color3> {
Ok(Color3::new(
read_f32(&mut reader)?,
read_f32(&mut reader)?,
read_f32(&mut reader)?,
))
}
fn read_udim<R: Read>(mut reader: R) -> io::Result<UDim> {
Ok(UDim::new(read_f32(&mut reader)?, read_i32(&mut reader)?))
}
fn read_vector2<R: Read>(mut reader: R) -> io::Result<Vector2> {
Ok(Vector2::new(read_f32(&mut reader)?, read_f32(&mut reader)?))
}
fn read_exact_or_none<R: Read>(mut reader: R, mut buf: &mut [u8]) -> io::Result<bool> {
let initial_len = buf.len();
while !buf.is_empty() {
match reader.read(buf) {
Ok(0) => break,
Ok(n) => {
let tmp = buf;
buf = &mut tmp[n..];
}
Err(e) if e.kind() == io::ErrorKind::Interrupted => {}
Err(e) => return Err(e),
}
}
if buf.len() == initial_len {
Ok(false)
} else if !buf.is_empty() {
Err(io::Error::new(
io::ErrorKind::UnexpectedEof,
"failed to fill whole buffer",
))
} else {
Ok(true)
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
#[allow(clippy::bool_assert_comparison)]
fn exact_or_none() {
let mut buf = [0u8; 4];
assert_eq!(read_exact_or_none(&[][..], &mut buf).unwrap(), false);
assert!(read_exact_or_none(&[0][..], &mut buf).is_err());
assert!(read_exact_or_none(&[0, 1][..], &mut buf).is_err());
assert!(read_exact_or_none(&[0, 1, 2][..], &mut buf).is_err());
assert_eq!(
read_exact_or_none(&[0, 1, 2, 3][..], &mut buf).unwrap(),
true
);
assert_eq!(
read_exact_or_none(&[0, 1, 2, 3, 4][..], &mut buf).unwrap(),
true
);
}
}