use super::traits::{ReaderExt as _, WriterExt as _};
use super::Error;
use byteorder::{ReadBytesExt as _, WriteBytesExt as _, LE};
use num_enum::{IntoPrimitive, TryFromPrimitive};
use std::io;
pub mod value;
pub use value::PropertyValueEnum;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(
Clone,
Copy,
Debug,
PartialEq,
Eq,
PartialOrd,
Ord,
Hash,
IntoPrimitive,
TryFromPrimitive,
Default,
)]
#[repr(u8)]
pub enum BinPropertyKind {
#[default]
None = 0,
Bool = 1,
I8 = 2,
U8 = 3,
I16 = 4,
U16 = 5,
I32 = 6,
U32 = 7,
I64 = 8,
U64 = 9,
F32 = 10,
Vector2 = 11,
Vector3 = 12,
Vector4 = 13,
Matrix44 = 14,
Color = 15,
String = 16,
Hash = 17,
WadChunkLink = 18,
Container = 128,
UnorderedContainer = 128 | 1,
Struct = 128 | 2,
Embedded = 128 | 3,
ObjectLink = 128 | 4,
Optional = 128 | 5,
Map = 128 | 6,
BitBool = 128 | 7,
}
impl BinPropertyKind {
pub fn unpack(raw: u8, legacy: bool) -> Result<BinPropertyKind, Error> {
use BinPropertyKind as BPK;
if !legacy {
return Ok(BPK::try_from_primitive(raw)?);
}
let mut fudged = raw;
if fudged >= BPK::WadChunkLink.into() && fudged < BPK::Container.into() {
fudged -= Into::<u8>::into(BPK::WadChunkLink);
fudged |= Into::<u8>::into(BPK::Container);
}
if fudged >= BPK::UnorderedContainer.into() {
fudged += 1;
}
Ok(BinPropertyKind::try_from_primitive(fudged)?)
}
#[inline(always)]
#[must_use]
pub fn is_primitive(&self) -> bool {
use BinPropertyKind::*;
matches!(
self,
None | Bool
| I8
| U8
| I16
| U16
| I32
| U32
| I64
| U64
| F32
| Vector2
| Vector3
| Vector4
| Matrix44
| Color
| String
| Hash
| WadChunkLink
)
}
#[inline(always)]
#[must_use]
pub fn is_container(&self) -> bool {
self.subtype_count() > 0
}
#[inline(always)]
#[must_use]
pub fn subtype_count(&self) -> u8 {
use BinPropertyKind::*;
match self {
Container | UnorderedContainer | Optional => 1,
Map => 2,
_ => 0,
}
}
#[inline(always)]
pub fn read<R: io::Read + std::io::Seek + ?Sized>(
self,
reader: &mut R,
legacy: bool,
) -> Result<PropertyValueEnum, super::Error> {
PropertyValueEnum::from_reader(reader, self, legacy)
}
pub fn default_value(self) -> PropertyValueEnum {
match self {
Self::None => PropertyValueEnum::None(Default::default()),
Self::Bool => PropertyValueEnum::Bool(Default::default()),
Self::I8 => PropertyValueEnum::I8(Default::default()),
Self::U8 => PropertyValueEnum::U8(Default::default()),
Self::I16 => PropertyValueEnum::I16(Default::default()),
Self::U16 => PropertyValueEnum::U16(Default::default()),
Self::I32 => PropertyValueEnum::I32(Default::default()),
Self::U32 => PropertyValueEnum::U32(Default::default()),
Self::I64 => PropertyValueEnum::I64(Default::default()),
Self::U64 => PropertyValueEnum::U64(Default::default()),
Self::F32 => PropertyValueEnum::F32(Default::default()),
Self::Vector2 => PropertyValueEnum::Vector2(Default::default()),
Self::Vector3 => PropertyValueEnum::Vector3(Default::default()),
Self::Vector4 => PropertyValueEnum::Vector4(Default::default()),
Self::Matrix44 => PropertyValueEnum::Matrix44(Default::default()),
Self::Color => PropertyValueEnum::Color(Default::default()),
Self::String => PropertyValueEnum::String(Default::default()),
Self::Hash => PropertyValueEnum::Hash(Default::default()),
Self::WadChunkLink => PropertyValueEnum::WadChunkLink(Default::default()),
Self::Container => PropertyValueEnum::Container(Default::default()),
Self::UnorderedContainer => PropertyValueEnum::UnorderedContainer(Default::default()),
Self::Struct => PropertyValueEnum::Struct(Default::default()),
Self::Embedded => PropertyValueEnum::Embedded(Default::default()),
Self::ObjectLink => PropertyValueEnum::ObjectLink(Default::default()),
Self::Optional => PropertyValueEnum::Optional(Default::default()),
Self::Map => PropertyValueEnum::Map(Default::default()),
Self::BitBool => PropertyValueEnum::BitBool(Default::default()),
}
}
}
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Clone, PartialEq, Debug)]
pub struct BinProperty {
pub name_hash: u32,
#[cfg_attr(feature = "serde", serde(flatten))]
pub value: PropertyValueEnum,
}
use super::traits::PropertyValue as _;
impl BinProperty {
pub fn from_reader<R: io::Read + std::io::Seek + ?Sized>(
reader: &mut R,
legacy: bool,
) -> Result<Self, Error> {
let name_hash = reader.read_u32::<LE>()?;
let kind = reader.read_property_kind(legacy)?;
Ok(Self {
name_hash,
value: PropertyValueEnum::from_reader(reader, kind, legacy)?,
})
}
pub fn to_writer<W: io::Write + std::io::Seek + ?Sized>(
&self,
writer: &mut W,
) -> Result<(), io::Error> {
writer.write_u32::<LE>(self.name_hash)?;
writer.write_property_kind(self.value.kind())?;
self.value.to_writer(writer)?;
Ok(())
}
pub fn size(&self) -> usize {
5 + self.value.size_no_header()
}
}