use crate::{
asset::{self, uncompressed::UncompressedFrame},
quantized, rotation, Uncompressed,
};
use byteorder::{ReadBytesExt, LE};
use glam::Vec3;
use ltk_hash::elf;
use ltk_io_ext::{untrusted::UntrustedCapacity, ReaderExt};
use std::collections::{hash_map::Entry, HashMap};
use std::io::{Read, Seek, SeekFrom};
fn section_count(
section_name: &'static str,
start: i32,
end: i32,
element_size: usize,
) -> asset::Result<usize> {
let size = usize::try_from(end - start).map_err(|_| {
asset::AssetParseError::InvalidField(
section_name,
format!("ends at offset {end}, before its start at offset {start}"),
)
})?;
if !size.is_multiple_of(element_size) {
return Err(asset::AssetParseError::InvalidField(
section_name,
format!(
"invalid size {}; expected multiple of {} bytes",
size, element_size
),
));
}
Ok(size / element_size)
}
fn clip_timing(frame_count: usize, frame_duration: f32) -> asset::Result<(f32, f32)> {
let fps = 1.0 / frame_duration;
let duration = frame_count as f32 * frame_duration;
if frame_duration > 0.0 && frame_duration.is_finite() && fps.is_finite() && duration.is_finite()
{
Ok((fps, duration))
} else {
Err(asset::AssetParseError::InvalidField(
"frame duration",
frame_duration.to_string(),
))
}
}
impl Uncompressed {
pub fn from_reader<R: Read + Seek + ?Sized>(reader: &mut R) -> asset::Result<Self> {
let _magic = reader.read_u64::<LE>()?; let version = reader.read_u32::<LE>()?;
match version {
5 => Self::read_v5(reader),
4 => Self::read_v4(reader),
3 => Self::read_v3_legacy(reader),
_ => Err(asset::AssetParseError::InvalidFileVersion(version)),
}
}
fn read_v5<R: Read + Seek + ?Sized>(reader: &mut R) -> asset::Result<Self> {
let _resource_size = reader.read_u32::<LE>()?;
let _format_token = reader.read_u32::<LE>()?;
let _version = reader.read_u32::<LE>()?;
let _flags = reader.read_u32::<LE>()?;
let track_count = reader.read_u32::<LE>()? as usize;
let frame_count = reader.read_u32::<LE>()? as usize;
let frame_duration = reader.read_f32::<LE>()?;
let (fps, duration) = clip_timing(frame_count, frame_duration)?;
let joint_hashes_offset = reader.read_i32::<LE>()?;
let _asset_name_offset = reader.read_i32::<LE>()?;
let _time_offset = reader.read_i32::<LE>()?;
let vector_palette_offset = reader.read_i32::<LE>()?;
let quat_palette_offset = reader.read_i32::<LE>()?;
let frames_offset = reader.read_i32::<LE>()?;
if joint_hashes_offset <= 0 {
return Err(asset::AssetParseError::MissingData("joint hashes"));
}
if vector_palette_offset <= 0 {
return Err(asset::AssetParseError::MissingData("vector palette"));
}
if quat_palette_offset <= 0 {
return Err(asset::AssetParseError::MissingData("quaternion palette"));
}
if frames_offset <= 0 {
return Err(asset::AssetParseError::MissingData("frames"));
}
let joint_hash_count =
section_count("joint hashes", joint_hashes_offset, frames_offset, 4)?;
let vector_count = section_count(
"vector palette",
vector_palette_offset,
quat_palette_offset,
12,
)?;
let quat_count = section_count(
"quaternion palette",
quat_palette_offset,
joint_hashes_offset,
6,
)?;
if joint_hash_count > track_count {
return Err(asset::AssetParseError::InvalidField(
"joint hashes",
format!("{joint_hash_count} joint hashes for {track_count} tracks"),
));
}
reader.seek(SeekFrom::Start(joint_hashes_offset as u64 + 12))?;
let mut joint_hashes = Vec::with_untrusted_capacity(joint_hash_count);
for _ in 0..joint_hash_count {
joint_hashes.push(reader.read_u32::<LE>()?);
}
reader.seek(SeekFrom::Start(vector_palette_offset as u64 + 12))?;
let mut vector_palette = Vec::with_untrusted_capacity(vector_count);
for _ in 0..vector_count {
vector_palette.push(reader.read_vec3::<LE>()?);
}
reader.seek(SeekFrom::Start(quat_palette_offset as u64 + 12))?;
let mut quat_palette = Vec::with_untrusted_capacity(quat_count);
for _ in 0..quat_count {
let mut bytes = [0u8; 6];
reader.read_exact(&mut bytes)?;
quat_palette.push(quantized::decompress_quat(&bytes).normalize());
}
let mut tracks = vec![Vec::new(); joint_hashes.len()];
reader.seek(SeekFrom::Start(frames_offset as u64 + 12))?;
for _ in 0..frame_count {
for track_id in 0..track_count {
let translation_id = reader.read_u16::<LE>()?;
let scale_id = reader.read_u16::<LE>()?;
let rotation_id = reader.read_u16::<LE>()?;
if let Some(frames) = tracks.get_mut(track_id) {
frames.push(UncompressedFrame {
translation_id,
scale_id,
rotation_id,
});
}
}
}
let joint_frames: HashMap<u32, Vec<UncompressedFrame>> =
joint_hashes.into_iter().zip(tracks).collect();
Ok(Self {
duration,
fps,
frame_count,
vector_palette,
quat_palette,
joint_frames,
})
}
fn read_v4<R: Read + Seek + ?Sized>(reader: &mut R) -> asset::Result<Self> {
let _resource_size = reader.read_u32::<LE>()?;
let _format_token = reader.read_u32::<LE>()?;
let _version = reader.read_u32::<LE>()?;
let _flags = reader.read_u32::<LE>()?;
let track_count = reader.read_u32::<LE>()? as usize;
let frame_count = reader.read_u32::<LE>()? as usize;
let frame_duration = reader.read_f32::<LE>()?;
let (fps, duration) = clip_timing(frame_count, frame_duration)?;
let _joint_hashes_offset = reader.read_i32::<LE>()?;
let _asset_name_offset = reader.read_i32::<LE>()?;
let _time_offset = reader.read_i32::<LE>()?;
let vector_palette_offset = reader.read_i32::<LE>()?;
let quat_palette_offset = reader.read_i32::<LE>()?;
let frames_offset = reader.read_i32::<LE>()?;
if vector_palette_offset <= 0 {
return Err(asset::AssetParseError::MissingData("vector palette"));
}
if quat_palette_offset <= 0 {
return Err(asset::AssetParseError::MissingData("quaternion palette"));
}
if frames_offset <= 0 {
return Err(asset::AssetParseError::MissingData("frames"));
}
let vector_count = section_count(
"vector palette",
vector_palette_offset,
quat_palette_offset,
12,
)?;
let quat_count = section_count(
"quaternion palette",
quat_palette_offset,
frames_offset,
16, )?;
reader.seek(SeekFrom::Start(vector_palette_offset as u64 + 12))?;
let mut vector_palette = Vec::with_untrusted_capacity(vector_count);
for _ in 0..vector_count {
vector_palette.push(reader.read_vec3::<LE>()?);
}
reader.seek(SeekFrom::Start(quat_palette_offset as u64 + 12))?;
let mut quat_palette = Vec::with_untrusted_capacity(quat_count);
for _ in 0..quat_count {
let quat = reader.read_quat::<LE>()?;
let quat = rotation::try_normalize(quat).ok_or_else(|| {
asset::AssetParseError::InvalidField("quaternion palette", quat.to_string())
})?;
quat_palette.push(quat);
}
let mut joint_frames: HashMap<u32, Vec<UncompressedFrame>> =
HashMap::with_untrusted_capacity(track_count);
reader.seek(SeekFrom::Start(frames_offset as u64 + 12))?;
for frame_id in 0..frame_count {
for _ in 0..track_count {
let joint_hash = reader.read_u32::<LE>()?;
let translation_id = reader.read_u16::<LE>()?;
let scale_id = reader.read_u16::<LE>()?;
let rotation_id = reader.read_u16::<LE>()?;
let _padding = reader.read_u16::<LE>()?;
let joint_count = joint_frames.len();
let frames = match joint_frames.entry(joint_hash) {
Entry::Occupied(entry) => entry.into_mut(),
Entry::Vacant(entry) if joint_count < track_count => entry.insert(Vec::new()),
Entry::Vacant(_) => {
return Err(asset::AssetParseError::InvalidField(
"joint hashes",
format!("more joint hashes than the {track_count} tracks"),
));
}
};
if frames.len() <= frame_id {
frames.resize(frame_id + 1, UncompressedFrame::default());
}
frames[frame_id] = UncompressedFrame {
translation_id,
scale_id,
rotation_id,
};
}
}
for frames in joint_frames.values_mut() {
frames.resize(frame_count, UncompressedFrame::default());
}
Ok(Self {
duration,
fps,
frame_count,
vector_palette,
quat_palette,
joint_frames,
})
}
fn read_v3_legacy<R: Read + Seek + ?Sized>(reader: &mut R) -> asset::Result<Self> {
let _skeleton_id = reader.read_u32::<LE>()?;
let track_count = reader.read_u32::<LE>()? as usize;
let frame_count = reader.read_u32::<LE>()? as usize;
let fps = reader.read_u32::<LE>()? as f32;
let duration = frame_count as f32 / fps;
let key_count = frame_count.saturating_mul(track_count);
let mut quat_palette = Vec::with_untrusted_capacity(key_count);
let mut vector_palette = Vec::with_untrusted_capacity(key_count.saturating_add(1));
let mut joint_frames: HashMap<u32, Vec<UncompressedFrame>> =
HashMap::with_untrusted_capacity(track_count);
vector_palette.push(Vec3::ONE);
for _ in 0..track_count {
let joint_name: String = reader.read_padded_string::<LE, 32>()?;
let joint_hash = elf::elf(&joint_name) as u32;
let _flags = reader.read_u32::<LE>()?;
let mut frames = Vec::with_untrusted_capacity(frame_count);
for _ in 0..frame_count {
let rotation = reader.read_quat::<LE>()?;
let translation = reader.read_vec3::<LE>()?;
let rotation_id = quat_palette.len() as u16;
quat_palette.push(rotation);
let translation_id = vector_palette.len() as u16;
vector_palette.push(translation);
frames.push(UncompressedFrame {
translation_id,
scale_id: 0,
rotation_id,
});
}
joint_frames.insert(joint_hash, frames);
}
Ok(Self {
duration,
fps,
frame_count,
vector_palette,
quat_palette,
joint_frames,
})
}
}