use crate::renderware::rw_file::RwFile;
use crate::renderware::common::types::{RwVector3, RwQuaternion};
use std::io::Result;
use serde::Serialize;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize)]
pub enum IfpVersion {
ANP3,
ANPK,
UNSUPPORTED,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct RwIfp {
pub version: IfpVersion,
pub name: String,
pub animations: Vec<RwIfpAnimation>,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct RwIfpAnimation {
pub name: String,
pub bones: Vec<RwIfpBone>,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct RwIfpBone {
pub name: String,
pub keyframe_type: String,
pub use_bone_id: bool,
pub bone_id: i32,
pub keyframes: Vec<RwIfpKeyframe>,
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
pub struct RwIfpKeyframe {
pub time: f32,
pub position: RwVector3,
pub rotation: RwQuaternion,
pub scale: RwVector3,
}
pub struct IfpParser<'a> {
file: RwFile<'a>,
}
impl<'a> IfpParser<'a> {
pub fn new(buffer: &'a [u8]) -> Self {
IfpParser {
file: RwFile::new(buffer),
}
}
pub fn parse(&mut self) -> Result<RwIfp> {
let file_signature = self.file.get_stream().read_string(4)?;
self.file.get_stream().set_position(0);
match file_signature.as_str() {
"ANP3" => self.read_anp3(),
"ANPK" => self.read_anpk(),
_ => Err(std::io::Error::new(std::io::ErrorKind::Other, "Unsupported IFP version")),
}
}
fn read_anp3(&mut self) -> Result<RwIfp> {
self.file.get_stream().skip(4)?; let _size = self.file.get_stream().read_u32()?;
let name = self.file.get_stream().read_string(24)?;
let animations_count = self.file.get_stream().read_u32()?;
let mut animations = Vec::with_capacity(animations_count as usize);
for _ in 0..animations_count {
animations.push(self.read_anp3_animation()?);
}
Ok(RwIfp {
version: IfpVersion::ANP3,
name,
animations,
})
}
fn read_anp3_animation(&mut self) -> Result<RwIfpAnimation> {
let name = self.file.get_stream().read_string(24)?;
let bones_count = self.file.get_stream().read_u32()?;
self.file.get_stream().skip(8)?; let mut bones = Vec::with_capacity(bones_count as usize);
for _ in 0..bones_count {
bones.push(self.read_anp3_bone()?);
}
Ok(RwIfpAnimation { name, bones })
}
fn read_anp3_bone(&mut self) -> Result<RwIfpBone> {
let name = self.file.get_stream().read_string(24)?;
let keyframe_type_num = self.file.get_stream().read_u32()?;
let keyframes_count = self.file.get_stream().read_u32()?;
let keyframe_type = if keyframe_type_num == 4 { "KRT0" } else { "KR00" }.to_string();
let bone_id = self.file.get_stream().read_i32()?;
let mut keyframes = Vec::with_capacity(keyframes_count as usize);
for _ in 0..keyframes_count {
let qx = self.file.get_stream().read_i16()? as f32 / 4096.0;
let qy = self.file.get_stream().read_i16()? as f32 / 4096.0;
let qz = self.file.get_stream().read_i16()? as f32 / 4096.0;
let qw = self.file.get_stream().read_i16()? as f32 / 4096.0;
let time = self.file.get_stream().read_i16()? as f32;
let (px, py, pz) = if keyframe_type.as_bytes()[2] == b'T' {
(
self.file.get_stream().read_i16()? as f32 / 1024.0,
self.file.get_stream().read_i16()? as f32 / 1024.0,
self.file.get_stream().read_i16()? as f32 / 1024.0,
)
} else {
(0.0, 0.0, 0.0)
};
keyframes.push(RwIfpKeyframe {
time,
position: RwVector3 { x: px, y: py, z: pz },
rotation: RwQuaternion { w: qw, x: qx, y: qy, z: qz },
scale: RwVector3 { x: 1.0, y: 1.0, z: 1.0 },
});
}
Ok(RwIfpBone {
name,
keyframe_type,
use_bone_id: true,
bone_id,
keyframes,
})
}
fn read_anpk(&mut self) -> Result<RwIfp> {
self.file.get_stream().skip(4)?; let _size = self.file.get_stream().read_u32()?;
self.file.get_stream().skip(4)?; let info_len = self.file.get_stream().read_u32()?;
let animations_count = self.file.get_stream().read_u32()?;
let name = self.file.get_stream().read_string((info_len - 4) as usize)?;
let name_align_len = (4 - info_len % 4) % 4;
self.file.get_stream().skip(name_align_len as u64)?;
let mut animations = Vec::with_capacity(animations_count as usize);
for _ in 0..animations_count {
animations.push(self.read_anpk_animation()?);
}
Ok(RwIfp {
version: IfpVersion::ANPK,
name,
animations,
})
}
fn read_anpk_animation(&mut self) -> Result<RwIfpAnimation> {
self.file.get_stream().skip(4)?; let name_len = self.file.get_stream().read_u32()?;
let name = self.file.get_stream().read_string(name_len as usize)?;
self.file.get_stream().skip(((4 - name_len % 4) % 4) as u64)?;
self.file.get_stream().skip(16)?; let bones_count = self.file.get_stream().read_u32()?;
self.file.get_stream().skip(4)?;
let mut bones = Vec::with_capacity(bones_count as usize);
for _ in 0..bones_count {
bones.push(self.read_anpk_bone()?);
}
Ok(RwIfpAnimation { name, bones })
}
fn read_anpk_bone(&mut self) -> Result<RwIfpBone> {
self.file.get_stream().skip(8)?; self.file.get_stream().skip(4)?; let anim_len = self.file.get_stream().read_u32()?;
let name = self.file.get_stream().read_string(28)?;
let keyframes_count = self.file.get_stream().read_u32()?;
self.file.get_stream().skip(8)?;
let use_bone_id = anim_len == 44;
let bone_id = if use_bone_id {
self.file.get_stream().read_i32()?
} else {
self.file.get_stream().skip(8)?;
0
};
let mut keyframe_type = "K000".to_string();
let mut keyframes = Vec::new();
if keyframes_count > 0 {
keyframe_type = self.file.get_stream().read_string(4)?;
self.file.get_stream().skip(4)?;
for _ in 0..keyframes_count {
let qx = self.file.get_stream().read_f32()?;
let qy = self.file.get_stream().read_f32()?;
let qz = self.file.get_stream().read_f32()?;
let qw = self.file.get_stream().read_f32()?;
let (px, py, pz) = if keyframe_type.as_bytes()[2] == b'T' {
(self.file.get_stream().read_f32()?, self.file.get_stream().read_f32()?, self.file.get_stream().read_f32()?)
} else {
(0.0, 0.0, 0.0)
};
let (sx, sy, sz) = if keyframe_type.as_bytes()[3] == b'S' {
(self.file.get_stream().read_f32()?, self.file.get_stream().read_f32()?, self.file.get_stream().read_f32()?)
} else {
(1.0, 1.0, 1.0)
};
let time = self.file.get_stream().read_f32()?;
keyframes.push(RwIfpKeyframe {
time,
position: RwVector3 { x: px, y: py, z: pz },
rotation: RwQuaternion { w: qw, x: qx, y: qy, z: qz },
scale: RwVector3 { x: sx, y: sy, z: sz },
});
}
}
Ok(RwIfpBone {
name,
keyframe_type,
use_bone_id,
bone_id,
keyframes,
})
}
}