use binrw::io::SeekFrom;
use crate::mesh::BoundingSphere;
use crate::{CString, Ptr64, Vector3};
use binrw::{binread, BinRead};
use modular_bitfield::prelude::*;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
use ssbh_write::SsbhWrite;
#[binread]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, Clone, PartialEq)]
pub struct MeshEx {
#[br(temp)]
_file_length: u64,
#[br(temp)]
entry_count: u32,
#[br(temp)]
mesh_object_group_count: u32,
pub all_data: Ptr64<AllData>,
#[br(count = mesh_object_group_count)]
pub mesh_object_groups: Ptr64<Vec<MeshObjectGroup>>,
#[br(count = entry_count)]
pub entries: Ptr64<Vec<MeshEntry>>,
#[br(args(entry_count as usize))]
pub entry_flags: Ptr64<EntryFlags>,
pub unk1: u32,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, BinRead, SsbhWrite, Clone, PartialEq)]
#[ssbhwrite(alignment = 16)]
pub struct MeshEntry {
pub mesh_object_group_index: u32,
pub unk1: Vector3,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, BinRead, SsbhWrite, Clone, PartialEq)]
#[ssbhwrite(alignment = 16)]
pub struct AllData {
pub bounding_sphere: BoundingSphere,
pub name: Ptr64<CString<16>>,
}
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, BinRead, SsbhWrite, Clone, PartialEq)]
#[ssbhwrite(alignment = 16)]
pub struct MeshObjectGroup {
pub bounding_sphere: BoundingSphere,
pub mesh_object_full_name: Ptr64<CString<4>>,
pub mesh_object_name: Ptr64<CString<4>>,
}
#[bitfield(bits = 16)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, BinRead, Clone, Copy, PartialEq, Eq)]
#[br(map = Self::from_bytes)]
pub struct EntryFlag {
pub draw_model: bool,
pub cast_shadow: bool,
#[skip]
__: bool,
pub unk3: bool,
pub unk4: bool,
pub unk5: bool,
#[skip]
__: B10,
}
ssbh_write::ssbh_write_modular_bitfield_impl!(EntryFlag, 2);
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
#[derive(Debug, BinRead, SsbhWrite, Clone, PartialEq)]
#[ssbhwrite(alignment = 16)]
#[br(import(count: usize))]
pub struct EntryFlags(#[br(count = count)] pub Vec<EntryFlag>);
impl SsbhWrite for MeshEx {
fn ssbh_write<W: std::io::Write + std::io::Seek>(
&self,
writer: &mut W,
data_ptr: &mut u64,
) -> std::io::Result<()> {
let entry_count = self
.entries
.as_ref()
.map(|e| e.len() as u32)
.unwrap_or(0u32);
let entry_flag_count = self
.entry_flags
.as_ref()
.map(|e| e.0.len() as u32)
.unwrap_or(0u32);
if entry_count != entry_flag_count {
return Err(std::io::Error::new(
std::io::ErrorKind::Other,
format!("Inconsistent entry count: {entry_count} != {entry_flag_count}"),
));
}
let current_pos = writer.stream_position()?;
if *data_ptr < current_pos + self.size_in_bytes() {
*data_ptr = current_pos + self.size_in_bytes();
}
(0u64).ssbh_write(writer, data_ptr)?;
entry_count.ssbh_write(writer, data_ptr)?;
self.mesh_object_groups
.as_ref()
.map(|g| g.len() as u32)
.unwrap_or(0u32)
.ssbh_write(writer, data_ptr)?;
self.all_data.ssbh_write(writer, data_ptr)?;
self.mesh_object_groups.ssbh_write(writer, data_ptr)?;
self.entries.ssbh_write(writer, data_ptr)?;
self.entry_flags.ssbh_write(writer, data_ptr)?;
self.unk1.ssbh_write(writer, data_ptr)?;
let round_up = |value, n| ((value + n - 1) / n) * n;
let size = writer.seek(SeekFrom::End(0))?;
let new_size = round_up(size, 16);
writer.write_all(&vec![0u8; (new_size - size) as usize])?;
writer.rewind()?;
new_size.ssbh_write(writer, data_ptr)?;
Ok(())
}
fn size_in_bytes(&self) -> u64 {
64
}
}