use std::io::Cursor;
use std::io::SeekFrom;
use crate::ByteSpan;
use crate::ReadableFile;
use crate::common::Platform;
use crate::common_file_operations::read_bool_from;
use crate::common_file_operations::read_short_identifier;
use crate::common_file_operations::write_bool_as;
use crate::common_file_operations::write_short_identifier;
use crate::string_heap::HeapPointer;
use crate::string_heap::HeapString;
use crate::string_heap::StringHeap;
use binrw::BinRead;
use binrw::BinReaderExt;
use binrw::BinResult;
use binrw::VecArgs;
use binrw::binrw;
#[binrw::parser(reader, endian)]
pub(crate) fn read_timeline_list(struct_offset: i64) -> BinResult<Vec<u16>> {
let offset: i32 = reader.read_type_args(endian, ())?;
let count: i32 = reader.read_type_args(endian, ())?;
let pos = reader.stream_position()?;
reader.seek(SeekFrom::Current(offset as i64 - 8_i64 - struct_offset))?;
let list =
reader.read_type_args(endian, VecArgs::builder().count(count as usize).finalize())?;
reader.seek(SeekFrom::Start(pos))?;
Ok(list)
}
#[binrw::parser(reader, endian)]
pub(crate) fn read_timeline_list_2(struct_offset: i64) -> BinResult<Vec<TmfcData>> {
let offset: i32 = reader.read_type_args(endian, ())?;
let count: i32 = reader.read_type_args(endian, ())?;
let pos = reader.stream_position()?;
reader.seek(SeekFrom::Current(offset as i64 - 4_i64 - struct_offset))?;
let mut list: Vec<TmfcData> =
reader.read_type_args(endian, VecArgs::builder().count(count as usize).finalize())?;
for data in &mut list {
data.rows = reader
.read_type_args(
endian,
VecArgs::builder().count(data.row_count as usize).finalize(),
)
.unwrap();
}
reader.seek(SeekFrom::Start(pos))?;
Ok(list)
}
#[binrw]
#[derive(Debug, Clone)]
#[repr(C)]
pub struct Tmdh {
unk1: u16,
unk2: u16,
pub duration: u16,
unk4: u16,
}
#[binrw]
#[derive(Debug)]
pub struct Tmal {
#[br(parse_with = read_timeline_list, args(0))]
pub tmac_ids: Vec<u16>,
}
#[binrw]
#[derive(Debug)]
pub struct Tmac {
pub id: u16,
pub time: u16,
unk1: u32, unk2: u32,
#[br(parse_with = read_timeline_list, args(12))]
pub tmtr_ids: Vec<u16>,
}
#[binrw]
#[derive(Debug)]
pub struct Tmtr {
pub id: u16,
pub time: u16, #[br(parse_with = read_timeline_list, args(4))]
pub animation_ids: Vec<u16>,
unk2: u32,
}
#[binrw]
#[br(import(string_heap: &StringHeap))]
#[bw(import(string_heap: &mut StringHeap))]
#[derive(Debug, Clone)]
#[repr(C)]
pub struct C009 {
pub id: u16,
pub time: u16,
pub duration: i32,
#[br(temp)]
#[bw(ignore)]
heap_pointer: HeapPointer,
unk1: i32,
#[br(args(heap_pointer, string_heap))]
#[bw(args(string_heap))]
pub path: HeapString,
}
#[binrw]
#[derive(Debug, Clone)]
#[repr(C)]
pub struct C013 {
pub id: u16,
pub time: u16,
pub duration: i32,
unk2: i32,
pub tmfc_id: i32,
placement: i32,
}
#[binrw]
#[derive(Debug, Clone)]
#[repr(C)]
pub struct C042 {
pub id: u16,
pub time: u16,
#[br(map = read_bool_from::<i32>)]
#[bw(map = write_bool_as::<i32>)]
pub enabled: bool,
unk2: i32,
foot_id: i32,
sound_id: i32,
}
#[binrw]
#[derive(Debug)]
pub struct Tmfc {
pub id: u16,
pub time: u16,
#[br(parse_with = read_timeline_list_2, args(4))]
pub data: Vec<TmfcData>,
unk1: i32,
end_offset: i32,
unk2: i32,
}
#[binrw]
#[derive(Debug)]
pub enum Attribute {
#[brw(magic = 64u8)]
PositionX,
#[brw(magic = 65u8)]
PositionY,
#[brw(magic = 66u8)]
PositionZ,
#[brw(magic = 67u8)]
RotationX,
#[brw(magic = 68u8)]
RotationY,
#[brw(magic = 69u8)]
RotationZ,
Unknown(u8),
}
#[binrw]
#[derive(Debug)]
pub struct TmfcData {
unk1: u32,
pub attribute: Attribute,
unk3: u8,
unk4: u8,
unk5: u8,
unk6: u8,
unk7: u8,
unk8: u8,
unk9: u8,
row_count: u32,
#[brw(ignore)] pub rows: Vec<TmfcRow>,
}
#[binrw]
#[repr(C)]
#[derive(Debug, Clone)]
pub struct TmfcRow {
unk1: u32,
pub time: f32,
velocity: f32, pub value: f32,
unk4: f32,
unk5: f32,
}
#[binrw]
#[br(import(tag: &str, size: u32, string_heap: &StringHeap))]
#[bw(import(string_heap: &mut StringHeap))]
#[derive(Debug)]
pub enum TimelineNodeData {
#[br(pre_assert(tag == "TMDH"))]
Tmdh(Tmdh),
#[br(pre_assert(tag == "TMAL"))]
Tmal(Tmal),
#[br(pre_assert(tag == "TMAC"))]
Tmac(Tmac),
#[br(pre_assert(tag == "TMTR"))]
Tmtr(Tmtr),
#[br(pre_assert(tag == "TMFC"))]
Tmfc(Tmfc),
#[br(pre_assert(tag == "C009"))]
C009(#[brw(args(string_heap,))] C009),
#[br(pre_assert(tag == "C013"))]
C013(C013),
#[br(pre_assert(tag == "C042"))]
C042(C042),
Unknown(#[br(count = size - 8)] Vec<u8>),
}
#[binrw]
#[br(import(string_heap: &StringHeap))]
#[bw(import(string_heap: &mut StringHeap))]
#[derive(Debug)]
pub struct TimelineNode {
#[bw(write_with = write_short_identifier)]
#[br(parse_with = read_short_identifier)]
tag: String,
size: u32,
#[br(args(&tag, size, string_heap,))]
#[bw(ignore)] pub data: TimelineNodeData,
}
#[binrw]
#[brw(magic = b"TMLB")]
#[br(import(string_heap: &StringHeap))]
#[bw(import(string_heap: &mut StringHeap))]
#[derive(Debug)]
pub struct Tmb {
file_size: u32,
num_nodes: u32,
#[br(count = num_nodes, args { inner: (string_heap,) })]
#[bw(ignore)]
pub nodes: Vec<TimelineNode>,
}
impl ReadableFile for Tmb {
fn from_existing(platform: Platform, buffer: ByteSpan) -> Option<Self> {
let mut cursor = Cursor::new(buffer);
let string_heap = StringHeap::from(0);
Tmb::read_options(&mut cursor, platform.endianness(), (&string_heap,)).ok()
}
}
#[cfg(test)]
mod tests {
use crate::pass_random_invalid;
use super::*;
#[test]
fn test_invalid() {
pass_random_invalid::<Tmb>();
}
}