use std::io::Read;
use anyhow::{Result, bail};
use crate::core::class_index::ClassIndex;
use crate::resource::prp::PlasmaRead;
use super::keys::{KeyFrame, KeyType, read_keys};
#[derive(Debug, Clone)]
pub enum Controller {
Leaf(LeafController),
Compound(CompoundController),
TM(TMController),
}
impl Controller {
pub fn read_creatable(reader: &mut impl Read) -> Result<Option<Self>> {
let class_idx = reader.read_u16()?;
if class_idx == 0x8000 {
return Ok(None); }
log::trace!("Controller: reading class 0x{:04X} ({})",
class_idx, crate::core::class_index::ClassIndex::class_name(class_idx));
match class_idx {
ClassIndex::PL_LEAF_CONTROLLER => {
Ok(Some(Controller::Leaf(LeafController::read(reader)?)))
}
ClassIndex::PL_COMPOUND_CONTROLLER => {
Ok(Some(Controller::Compound(CompoundController::read(reader)?)))
}
ClassIndex::PL_TMCONTROLLER => {
Ok(Some(Controller::TM(TMController::read(reader)?)))
}
ClassIndex::PL_COMPOUND_ROT_CONTROLLER | ClassIndex::PL_COMPOUND_POS_CONTROLLER => {
Ok(Some(Controller::Compound(CompoundController::read(reader)?)))
}
_ => {
bail!(
"Unknown controller class: 0x{:04X} ({})",
class_idx,
crate::core::class_index::ClassIndex::class_name(class_idx)
);
}
}
}
}
#[derive(Debug, Clone)]
pub struct LeafController {
pub key_type: KeyType,
pub keys: Vec<KeyFrame>,
}
impl LeafController {
pub fn read(reader: &mut impl Read) -> Result<Self> {
let type_byte = reader.read_u8()?;
let key_type = KeyType::from_u8(type_byte)?;
let num_keys = reader.read_u32()?;
let keys = read_keys(reader, key_type, num_keys)?;
Ok(Self { key_type, keys })
}
pub fn num_keys(&self) -> usize {
self.keys.len()
}
pub fn is_empty(&self) -> bool {
self.keys.is_empty()
}
}
#[derive(Debug, Clone)]
pub struct CompoundController {
pub x_controller: Option<Box<Controller>>,
pub y_controller: Option<Box<Controller>>,
pub z_controller: Option<Box<Controller>>,
}
impl CompoundController {
pub fn read(reader: &mut impl Read) -> Result<Self> {
let x = Controller::read_creatable(reader)?.map(Box::new);
let y = Controller::read_creatable(reader)?.map(Box::new);
let z = Controller::read_creatable(reader)?.map(Box::new);
Ok(Self {
x_controller: x,
y_controller: y,
z_controller: z,
})
}
}
#[derive(Debug, Clone)]
pub struct TMController {
pub pos_controller: Option<Box<Controller>>,
pub rot_controller: Option<Box<Controller>>,
pub scale_controller: Option<Box<Controller>>,
}
impl TMController {
pub fn read(reader: &mut impl Read) -> Result<Self> {
let pos = Controller::read_creatable(reader)?.map(Box::new);
let rot = Controller::read_creatable(reader)?.map(Box::new);
let scale = Controller::read_creatable(reader)?.map(Box::new);
Ok(Self {
pos_controller: pos,
rot_controller: rot,
scale_controller: scale,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
use std::io::Cursor;
#[test]
fn test_read_null_creatable() {
let data = [0x00, 0x80]; let mut cursor = Cursor::new(&data);
let ctrl = Controller::read_creatable(&mut cursor).unwrap();
assert!(ctrl.is_none());
}
#[test]
fn test_read_leaf_scalar() {
let mut data = Vec::new();
data.extend_from_slice(&ClassIndex::PL_LEAF_CONTROLLER.to_le_bytes());
data.push(3);
data.extend_from_slice(&2u32.to_le_bytes());
data.extend_from_slice(&0u16.to_le_bytes());
data.extend_from_slice(&0.0f32.to_le_bytes());
data.extend_from_slice(&30u16.to_le_bytes());
data.extend_from_slice(&1.0f32.to_le_bytes());
let mut cursor = Cursor::new(&data);
let ctrl = Controller::read_creatable(&mut cursor).unwrap().unwrap();
match ctrl {
Controller::Leaf(leaf) => {
assert_eq!(leaf.num_keys(), 2);
assert_eq!(leaf.key_type, KeyType::Scalar);
}
_ => panic!("Expected leaf controller"),
}
}
#[test]
fn test_read_compound() {
let mut data = Vec::new();
data.extend_from_slice(&ClassIndex::PL_COMPOUND_CONTROLLER.to_le_bytes());
data.extend_from_slice(&0x8000u16.to_le_bytes());
data.extend_from_slice(&ClassIndex::PL_LEAF_CONTROLLER.to_le_bytes());
data.push(3); data.extend_from_slice(&1u32.to_le_bytes());
data.extend_from_slice(&0u16.to_le_bytes());
data.extend_from_slice(&0.5f32.to_le_bytes());
data.extend_from_slice(&0x8000u16.to_le_bytes());
let mut cursor = Cursor::new(&data);
let ctrl = Controller::read_creatable(&mut cursor).unwrap().unwrap();
match ctrl {
Controller::Compound(compound) => {
assert!(compound.x_controller.is_none());
assert!(compound.y_controller.is_some());
assert!(compound.z_controller.is_none());
}
_ => panic!("Expected compound controller"),
}
}
}