use std::{
fs,
io::{self, Cursor, Read, Seek, Write},
mem,
path::Path,
};
use binrw::{binrw, BinReaderExt, BinResult, BinWrite};
use modular_bitfield::prelude::*;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
mod string;
pub use string::CString;
#[binrw]
#[brw(magic = b"EFFN")]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug)]
pub struct EffFile {
#[br(temp)]
#[bw(calc = 0x00020000)]
_version: u32,
#[br(temp)]
#[bw(calc = effect_handles.len() as i16)]
effect_handle_count: i16,
#[br(temp)]
#[bw(calc = effect_model_entries.len() as i16)]
effect_model_count: i16,
#[br(temp)]
#[bw(calc = effect_group_elements.len() as i16)]
effect_group_element_count: i16,
#[br(temp)]
#[bw(calc = self.calculate_resource_alignment_factor() as i16)]
resource_alignment_factor: i16,
#[br(count = effect_handle_count)]
pub effect_handles: Vec<EffectHandle>,
#[br(count = effect_group_element_count)]
pub effect_group_elements: Vec<EffectGroupElement>,
#[br(count = effect_model_count)]
pub effect_model_entries: Vec<EffectModelEntry>,
#[br(count = effect_handle_count)]
pub effect_handle_names: Vec<CString>,
#[br(count = effect_model_count)]
pub effect_model_names: Vec<CString>,
#[br(count = effect_group_element_count)]
pub parent_joint_names: Vec<CString>,
#[br(parse_with = binrw::helpers::until_eof)]
#[brw(if(resource_alignment_factor != -1), align_before = Self::calculate_resource_alignment(resource_alignment_factor))]
#[cfg_attr(feature = "serde", serde(skip))]
pub resource_data: Option<Vec<u8>>,
}
impl EffFile {
const RESOURCE_ALIGNMENT_COEFFICIENT: usize = 0x1000;
pub fn from_file<P: AsRef<Path>>(path: P) -> BinResult<Self> {
let mut file = Cursor::new(fs::read(path)?);
let eff = file.read_le::<Self>()?;
Ok(eff)
}
pub fn read<R: Read + Seek>(reader: &mut R) -> BinResult<Self> {
let eff = reader.read_le::<Self>()?;
Ok(eff)
}
pub fn write<W: Write + Seek>(&self, writer: &mut W) -> BinResult<()> {
self.write_le(writer)
}
pub fn write_to_file<P: AsRef<Path>>(&self, path: P) -> BinResult<()> {
let mut cursor = Cursor::new(Vec::new());
self.write_le(&mut cursor)?;
fs::write(path, cursor.get_mut())?;
Ok(())
}
pub fn write_resource_to_file<P: AsRef<Path>>(&self, path: P) -> io::Result<()> {
if let Some(resource_data) = &self.resource_data {
fs::write(path, resource_data)?;
}
Ok(())
}
fn calculate_resource_alignment_factor(&self) -> usize {
if self.resource_data.is_none() {
return usize::MAX;
}
let mut size = 0x10;
size += self.effect_handles.len() * mem::size_of::<EffectHandle>();
size += self.effect_group_elements.len() * mem::size_of::<EffectGroupElement>();
size += self.effect_model_entries.len() * mem::size_of::<EffectModelEntry>();
for name in self.effect_handle_names.iter() {
size += name.len() + 1;
}
for name in self.effect_model_names.iter() {
size += name.len() + 1;
}
for name in self.parent_joint_names.iter() {
size += name.len() + 1;
}
((size + Self::RESOURCE_ALIGNMENT_COEFFICIENT) & !0xFFF)
>> Self::RESOURCE_ALIGNMENT_COEFFICIENT.ilog2()
}
fn calculate_resource_alignment(factor: i16) -> usize {
if factor < mem::align_of::<u8>() as i16 {
return mem::align_of::<u8>();
}
factor as usize * Self::RESOURCE_ALIGNMENT_COEFFICIENT
}
}
#[binrw]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct EffectHandle {
pub flags: EffectHandleFlags,
pub emitter_set_handle: i32,
pub effect_model_entry_handle: i32,
pub effect_group_element_start: i16,
pub effect_group_element_count: i16,
}
#[bitfield(bits = 32)]
#[binrw]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, Copy, Eq, PartialEq)]
#[br(map = Self::from_bytes)]
#[bw(map = |b: &EffectHandleFlags| b.into_bytes())]
pub struct EffectHandleFlags {
pub unk_01: bool,
pub unk_02: bool,
pub unk_03: bool,
pub unk_04: bool,
pub unk_05: bool,
pub unk_06: bool,
pub unk_07: bool,
#[skip]
__: bool,
pub unk_09: bool,
pub unk_10: bool,
#[skip]
__: B2,
pub unk_13: bool,
pub unk_14: bool,
pub unk_15: bool,
pub unk_16: bool,
pub unk_17: bool,
#[skip]
__: bool,
pub hit_effect: bool,
pub unk_20: bool,
pub unk_21: bool,
#[skip]
__: bool,
pub unk_23: bool,
pub update_always: bool,
pub unk_25: bool,
pub unk_26: bool,
#[skip]
__: B2,
pub unk_29: bool,
pub unk_30: bool,
pub unk_31: bool,
pub unk_32: bool,
}
#[binrw]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct EffectGroupElement {
pub emitter_set_start_frame: i16,
pub emitter_set_handle: i16,
}
#[binrw]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[derive(Debug, Clone, PartialEq)]
pub struct EffectModelEntry {
pub unk: i8,
}