use std::io::SeekFrom;
use binrw::{BinResult, BinWrite, binrw};
use crate::{
common_file_operations::{read_bool_from, read_null_terminated_utf8, write_bool_as},
layer::Layer,
string_heap::{HeapPointer, HeapString, StringHeap},
tmb::Tmb,
};
#[binrw::writer(writer, endian)]
pub(crate) fn write_scns(scns: &Vec<ScnSection>, string_heap: &mut StringHeap) -> BinResult<()> {
for scn in scns {
scn.write_options(writer, endian, (string_heap,))?;
}
Ok(())
}
#[binrw]
#[br(import(string_heap: &StringHeap))]
#[bw(import(string_heap: &mut StringHeap))]
#[derive(Debug)]
pub struct ScnLayerGroup {
#[br(temp)]
#[bw(ignore)]
heap_pointer: HeapPointer,
pub layer_group_id: u32,
#[br(args(heap_pointer, string_heap))]
#[bw(args(string_heap))]
pub name: HeapString,
layer_offsets_start: i32,
layer_offsets_count: i32,
#[br(count = layer_offsets_count)]
#[br(seek_before = SeekFrom::Current(layer_offsets_start as i64 - ScnLayerGroup::SIZE as i64))]
#[br(restore_position)]
offsets_layers: Vec<i32>,
#[br(restore_position, parse_with = layers_from_offsets, args(&offsets_layers, string_heap))]
#[br(seek_before = SeekFrom::Current(layer_offsets_start as i64 - ScnLayerGroup::SIZE as i64))]
#[bw(ignore)] pub layers: Vec<Layer>,
}
impl ScnLayerGroup {
pub(crate) const SIZE: usize = 0x10;
}
#[binrw]
#[br(import(string_heap: &StringHeap))]
#[bw(import(string_heap: &mut StringHeap))]
#[derive(Debug)]
#[brw(magic = b"SCN1")]
pub struct ScnSection {
total_size: u32,
pub(crate) offset_layer_groups: i32,
pub(crate) num_layer_groups: i32,
offset_general: i32,
offset_layer_sets: i32,
offset_timelines: i32,
offset_layer_group_resources: i32,
num_layer_group_resources: i32,
unk2: i32,
offset_action_descriptors: i32,
unk4: i32,
unk5: i32,
offset_housing: i32,
unk7: i32,
unk8: i32,
unk9: i32,
unk10: i32,
offset_unk2: i32, offset_unk3: i32,
#[br(count = num_layer_groups, args { inner: (string_heap,) })]
#[br(seek_before = SeekFrom::Current(offset_layer_groups as i64 - ScnSection::SIZE as i64))]
#[br(restore_position)]
#[bw(ignore)] pub layer_groups: Vec<ScnLayerGroup>,
#[br(seek_before = SeekFrom::Current(offset_general as i64 - ScnSection::SIZE as i64))]
#[br(restore_position)]
#[brw(args(string_heap))]
pub general: ScnGeneralSection,
#[br(seek_before = SeekFrom::Current(offset_layer_sets as i64 - ScnSection::SIZE as i64))]
#[br(restore_position)]
#[brw(args(string_heap))]
pub layer_sets: ScnLayerSetsSection,
#[br(seek_before = SeekFrom::Current(offset_timelines as i64 - ScnSection::SIZE as i64))]
#[br(restore_position)]
#[br(args(string_heap))]
pub timelines: ScnTimelinesSection,
#[br(count = num_layer_group_resources)]
#[br(seek_before = SeekFrom::Current(offset_layer_group_resources as i64 - ScnSection::SIZE as i64))]
#[br(restore_position)]
offset_path_layer_group_resources: Vec<i32>,
#[br(parse_with = strings_from_offsets)]
#[br(args(&offset_path_layer_group_resources))]
#[br(restore_position)]
#[br(seek_before = SeekFrom::Current(offset_layer_group_resources as i64 - ScnSection::SIZE as i64))]
#[bw(ignore)] pub lgb_paths: Vec<String>,
#[br(seek_before = SeekFrom::Current(offset_action_descriptors as i64 - ScnSection::SIZE as i64))]
#[br(restore_position)]
pub action_descriptors: ScnSGActionDescriptors,
#[br(seek_before = SeekFrom::Current(offset_housing as i64 - ScnSection::SIZE as i64))]
#[br(restore_position)]
pub housing: ScnHousingSettings,
}
impl ScnSection {
pub(crate) const SIZE: usize = 0x48;
}
#[binrw]
#[br(import(string_heap: &StringHeap))]
#[bw(import(string_heap: &mut StringHeap))]
#[derive(Debug)]
pub struct ScnEnvSpace {
#[br(temp)]
#[bw(ignore)]
heap_pointer: HeapPointer,
#[br(args(heap_pointer, string_heap))]
#[bw(args(string_heap))]
pub envb_path: HeapString,
unk1: i32,
pub env_location_instance_id: i32,
#[br(args(heap_pointer, string_heap))]
#[bw(args(string_heap))]
pub essb_path: HeapString,
#[br(restore_position)]
unk: u64,
}
#[binrw]
#[br(import(string_heap: &StringHeap))]
#[bw(import(string_heap: &mut StringHeap))]
#[derive(Debug)]
pub struct ScnGeneralSection {
#[br(temp)]
#[bw(ignore)]
heap_pointer: HeapPointer,
unk9: i32,
#[br(args(heap_pointer, string_heap))]
#[bw(args(string_heap))]
pub bg_path: HeapString,
offset_env_spaces: i32,
num_env_spaces: i32,
unk1: i32,
#[br(args(heap_pointer, string_heap))]
#[bw(args(string_heap))]
pub svb_path: HeapString,
unk2: f32,
unk3: f32,
unk4: f32,
unk5: f32,
unk6: f32,
unk7: f32,
unk8: i32,
#[br(args(heap_pointer, string_heap))]
#[bw(args(string_heap))]
pub lcb_path: HeapString,
unk10: i32,
unk11: f32,
unk12: i32,
unk13: i32,
unk14: f32,
unk15: i32,
unk16: f32,
#[br(map = read_bool_from::<i32>)]
#[bw(map = write_bool_as::<i32>)]
have_lcbuw: bool,
#[br(count = num_env_spaces)]
#[br(seek_before = SeekFrom::Current(offset_env_spaces as i64 - ScnGeneralSection::SIZE as i64))]
#[br(restore_position)]
#[br(args { inner: (string_heap,) })]
#[bw(write_with = write_env_spaces, args(string_heap))]
pub env_spaces: Vec<ScnEnvSpace>,
}
#[binrw::writer(writer, endian)]
pub fn write_env_spaces(scns: &Vec<ScnEnvSpace>, string_heap: &mut StringHeap) -> BinResult<()> {
for scn in scns {
scn.write_options(writer, endian, (string_heap,))?;
}
Ok(())
}
impl ScnGeneralSection {
pub(crate) const SIZE: usize = 0x58;
}
#[binrw]
#[br(import(string_heap: &StringHeap))]
#[derive(Debug)]
pub struct ScnTimelinesSection {
offset_entries: i32,
num_entries: i32,
#[br(seek_before = SeekFrom::Current(offset_entries as i64 - ScnTimelinesSection::SIZE as i64))]
#[br(count = num_entries, restore_position, args { inner: (string_heap,) })]
#[bw(ignore)] pub timelines: Vec<ScnTimeline>,
}
impl ScnTimelinesSection {
pub(crate) const SIZE: usize = 0x8;
}
#[binrw]
#[br(import(string_heap: &StringHeap))]
#[bw(import(string_heap: &mut StringHeap))]
#[derive(Debug)]
pub struct ScnTimeline {
#[br(temp)]
#[bw(ignore)]
heap_pointer: HeapPointer,
pub sub_id: u32,
#[br(args(heap_pointer, string_heap))]
#[bw(args(string_heap))]
pub animation_type: HeapString,
offset_instances: i32,
num_instances: i32,
offset_action_timeline_key: i32, offset_tmb: i32,
tmb_size: i32,
unk1: [u8; 4], #[br(map = read_bool_from::<u8>)]
#[bw(map = write_bool_as::<u8>)]
pub auto_play: bool,
#[br(map = read_bool_from::<u8>)]
#[bw(map = write_bool_as::<u8>)]
pub loop_animation: bool,
unk2: [u8; 10],
#[br(seek_before = SeekFrom::Current(offset_tmb as i64 - ScnTimeline::SIZE as i64), restore_position)]
#[brw(args(string_heap,))]
pub tmb: Tmb,
#[br(seek_before = SeekFrom::Current(offset_instances as i64 - ScnTimeline::SIZE as i64))]
#[br(count = num_instances, restore_position)]
pub instances: Vec<ScnTimelineInstance>,
}
impl ScnTimeline {
pub(crate) const SIZE: usize = 0x2C;
}
#[binrw]
#[derive(Debug, Clone)]
#[repr(C)]
pub struct ScnTimelineInstance {
pub tmac_time: i32,
pub instance_id: i32,
}
#[binrw]
#[derive(Debug)]
pub struct ScnSGActionDescriptors {
unk: [u8; 16],
timeline_offset: i32,
unk2: [u8; 20],
count: i32, unk3: [u8; 4],
#[br(count = count)]
pub descriptors: Vec<ScnSGActionControllerDescriptor>,
}
#[binrw]
#[repr(C)]
#[derive(Debug, Clone)]
pub enum ScnSGActionControllerDescriptor {
#[brw(magic = 1i32)]
Door(ScnDoorActionDescription),
#[brw(magic = 2i32)]
Rotation(ScnRotationActionDescription),
Unknown(i32),
}
#[binrw]
#[repr(C)]
#[derive(Debug, Clone)]
pub struct ScnDoorActionDescription {
check_if_1: u8, unk: [u8; 11], pub door_object_0: u8,
pub door_object_1: u8,
unk2: [u8; 2],
pub door_type: i32,
unk1: f32,
pub max_rotation: f32,
pub max_translation: f32,
pub sound_0: u8,
pub sound_1: u8,
pub door_object_2: u8,
pub door_object_3: u8,
}
#[binrw]
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub enum RotationAxis {
#[brw(magic = 0i32)]
X,
#[brw(magic = 1i32)]
Y,
#[brw(magic = 2i32)]
Z,
}
#[binrw]
#[repr(C)]
#[derive(Debug, Clone)]
pub struct ScnRotationActionDescription {
check_if_1: u8, unk: [u8; 11], pub bg_part_id: u8,
unk2: [u8; 3], pub axis: RotationAxis,
pub duration: f32,
pub value: f32,
pub vfx_child1_id: u8,
#[br(map = read_bool_from::<u8>)]
#[bw(map = write_bool_as::<u8>)]
pub vfx_has_child1: bool,
unk_fields: [u8; 4], pub vfx_child_2_id: u8,
#[br(map = read_bool_from::<u8>)]
#[bw(map = write_bool_as::<u8>)]
pub vfx_has_child2: bool,
}
#[binrw]
#[derive(Debug)]
pub struct ScnUnknown3Section {
unk: [u8; 64],
}
#[binrw]
#[br(import(string_heap: &StringHeap))]
#[bw(import(string_heap: &mut StringHeap))]
#[derive(Debug)]
pub struct ScnLayerSetsSection {
offset: i32,
count: i32,
#[br(seek_before = SeekFrom::Current(offset as i64 - ScnLayerSetsSection::SIZE as i64))]
#[br(count = count, restore_position, args { inner: (string_heap,) })]
#[bw(write_with = write_layersets, args(string_heap))]
pub layer_sets: Vec<ScnLayerSet>,
}
#[binrw::writer(writer, endian)]
pub fn write_layersets(scns: &Vec<ScnLayerSet>, string_heap: &mut StringHeap) -> BinResult<()> {
for scn in scns {
scn.write_options(writer, endian, (string_heap,))?;
}
Ok(())
}
impl ScnLayerSetsSection {
pub(crate) const SIZE: usize = 0x8;
}
#[binrw]
#[br(import(string_heap: &StringHeap))]
#[bw(import(string_heap: &mut StringHeap))]
#[derive(Debug)]
pub struct ScnLayerSet {
#[br(temp)]
#[bw(ignore)]
heap_pointer: HeapPointer,
#[br(args(heap_pointer, string_heap))]
#[bw(args(string_heap))]
pub nvm_path: HeapString,
pub id: i32,
unk2: i32,
unk3: i32,
pub territory_type_id: u16,
pub content_finder_condition_id: u16,
unk5: i32,
#[br(args(heap_pointer, string_heap))]
#[bw(args(string_heap))]
pub nvx_path: HeapString,
}
#[binrw::parser(reader)]
fn strings_from_offsets(offsets: &Vec<i32>) -> BinResult<Vec<String>> {
let base_offset = reader.stream_position()?;
let mut strings: Vec<String> = vec![];
for offset in offsets {
let string_offset = *offset as u64;
reader.seek(SeekFrom::Start(base_offset + string_offset))?;
strings.push(read_null_terminated_utf8(reader));
}
Ok(strings)
}
#[binrw::parser(reader, endian)]
fn layers_from_offsets(offsets: &Vec<i32>, string_heap: &StringHeap) -> BinResult<Vec<Layer>> {
let base_offset = reader.stream_position()?;
let mut layers: Vec<Layer> = vec![];
for offset in offsets {
let layer_offset = *offset as u64;
reader.seek(SeekFrom::Start(base_offset + layer_offset))?;
layers.push(Layer::read(endian, reader, string_heap, string_heap).unwrap());
}
Ok(layers)
}
#[binrw]
#[derive(Debug)]
pub struct ScnHousingSettings {
default_color_id: u16,
unk1: u8,
unk2: u8,
unk3: u32,
unk4: [u32; 6],
unk5: u32,
unk6: u32,
unk7: u32,
unk8: u32,
unk9: u32,
unk10: u8,
unk11: [u8; 3],
unk12: u32,
unk13: u32,
unk14: u32,
}