#![allow(unused_variables)]
use std::io::{Read, Seek, SeekFrom};
use crate::common_file_operations::{read_bool_from, write_bool_as};
use crate::string_heap::{HeapPointer, HeapString, StringHeap};
use binrw::{BinRead, BinReaderExt};
use binrw::{Endian, binrw};
mod aetheryte;
pub use aetheryte::AetheryteInstanceObject;
mod bg;
pub use bg::{BGInstanceObject, ModelCollisionType};
mod collision;
pub use collision::{CollisionBoxInstanceObject, TriggerBoxInstanceObject, TriggerBoxShape};
mod common;
pub use common::{Color, ColorHDRI, GameInstanceObject};
mod env;
pub use env::{EnvLocationObject, EnvSetInstanceObject, EnvSetShape};
mod event;
pub use event::EventInstanceObject;
mod gathering;
pub use gathering::GatheringInstanceObject;
mod light;
pub use light::{LightInstanceObject, LightType, PointLightType};
mod marker;
pub use marker::{
ChairMarkerInstanceObject, ChairType, PositionMarkerInstanceObject, PositionMarkerType,
QuestMarkerInstanceObject, TargetMarkerInstanceObject, TargetMarkerType,
};
mod npc;
pub use npc::{BNPCInstanceObject, ENPCInstanceObject, NPCInstanceObject};
mod path;
pub use path::{
ClientPathInstanceObject, PathControlPoint, PathInstanceObject, ServerPathInstanceObject,
};
mod range;
pub use range::{
ClickableRangeInstanceObject, DoorRangeInstanceObject, EventRangeInstanceObject,
ExitRangeInstanceObject, ExitType, FateRangeInstanceObject, GimmickRangeInstanceObject,
MapRangeInstanceObject, PopRangeInstanceObject, PopType, PrefetchRangeInstanceObject,
};
mod shared_group;
pub use shared_group::{
ColourState, DoorState, RotationState, SharedGroupInstance, TransformState,
};
mod sound;
pub use sound::SoundInstanceObject;
mod transformation;
pub use transformation::Transformation;
mod treasure;
pub use treasure::TreasureInstanceObject;
mod vfx;
pub use vfx::{LineStyle, LineVFXInstanceObject, VFXInstanceObject};
#[binrw]
#[brw(repr = i32)]
#[repr(i32)]
#[derive(Debug, PartialEq)]
pub enum LayerEntryType {
None = 0x0,
BG = 0x1,
Attribute = 0x2,
LayLight = 0x3,
Vfx = 0x4,
PositionMarker = 0x5,
SharedGroup = 0x6,
Sound = 0x7,
EventNPC = 0x8,
BattleNPC = 0x9,
RoutePath = 0xA,
Character = 0xB,
Aetheryte = 0xC,
EnvSet = 0xD,
Gathering = 0xE,
HelperObject = 0xF,
Treasure = 0x10,
Clip = 0x11,
ClipCtrlPoint = 0x12,
ClipCamera = 0x13,
ClipLight = 0x14,
ClipReserve00 = 0x15,
ClipReserve01 = 0x16,
ClipReserve02 = 0x17,
ClipReserve03 = 0x18,
ClipReserve04 = 0x19,
ClipReserve05 = 0x1A,
ClipReserve06 = 0x1B,
ClipReserve07 = 0x1C,
ClipReserve08 = 0x1D,
ClipReserve09 = 0x1E,
ClipReserve10 = 0x1F,
ClipReserve11 = 0x20,
ClipReserve12 = 0x21,
ClipReserve13 = 0x22,
ClipReserve14 = 0x23,
CutAssetOnlySelectable = 0x24,
Player = 0x25,
Monster = 0x26,
Weapon = 0x27,
PopRange = 0x28,
ExitRange = 0x29,
Lvb = 0x2A,
MapRange = 0x2B,
NaviMeshRange = 0x2C,
EventObject = 0x2D,
DemiHuman = 0x2E,
EnvLocation = 0x2F,
ControlPoint = 0x30,
EventRange = 0x31,
RestBonusRange = 0x32,
QuestMarker = 0x33,
Timeline = 0x34,
ObjectBehaviorSet = 0x35,
Movie = 0x36,
ScenarioExd = 0x37,
ScenarioText = 0x38,
CollisionBox = 0x39,
DoorRange = 0x3A,
LineVFX = 0x3B,
SoundEnvSet = 0x3C,
CutActionTimeline = 0x3D,
CharaScene = 0x3E,
CutAction = 0x3F,
EquipPreset = 0x40,
ClientPath = 0x41,
ServerPath = 0x42,
GimmickRange = 0x43,
TargetMarker = 0x44,
ChairMarker = 0x45,
ClickableRange = 0x46,
PrefetchRange = 0x47,
FateRange = 0x48,
PartyMember = 0x49,
KeepRange = 0x4A,
SphereCastRange = 0x4B,
IndoorObject = 0x4C,
OutdoorObject = 0x4D,
EditGroup = 0x4E,
StableChocobo = 0x4F,
Unk4 = 0x53, Unk2 = 0x56, Unk5 = 0x57, Unk3 = 0x59, Unk1 = 0x5A, }
#[binrw]
#[derive(Debug, PartialEq)]
#[br(import(magic: &LayerEntryType, string_heap: &StringHeap, heap_pointer: HeapPointer))]
#[bw(import(string_heap: &mut StringHeap))]
pub enum LayerEntryData {
#[br(pre_assert(*magic == LayerEntryType::None))]
None,
#[br(pre_assert(*magic == LayerEntryType::BG))]
BG(
#[br(args(string_heap, heap_pointer))]
#[bw(args(string_heap))]
BGInstanceObject,
),
#[br(pre_assert(*magic == LayerEntryType::LayLight))]
LayLight(LightInstanceObject),
#[br(pre_assert(*magic == LayerEntryType::Vfx))]
Vfx(
#[br(args(string_heap, heap_pointer))]
#[bw(args(string_heap))]
VFXInstanceObject,
),
#[br(pre_assert(*magic == LayerEntryType::PositionMarker))]
PositionMarker(PositionMarkerInstanceObject),
#[br(pre_assert(*magic == LayerEntryType::SharedGroup))]
SharedGroup(
#[br(args(string_heap, heap_pointer))]
#[bw(args(string_heap))]
SharedGroupInstance,
),
#[br(pre_assert(*magic == LayerEntryType::Sound))]
Sound(
#[br(args(string_heap, heap_pointer))]
#[bw(args(string_heap))]
SoundInstanceObject,
),
#[br(pre_assert(*magic == LayerEntryType::EventNPC))]
EventNPC(ENPCInstanceObject),
#[br(pre_assert(*magic == LayerEntryType::BattleNPC))]
BattleNPC(BNPCInstanceObject),
#[br(pre_assert(*magic == LayerEntryType::Aetheryte))]
Aetheryte(AetheryteInstanceObject),
#[br(pre_assert(*magic == LayerEntryType::EnvSet))]
EnvSet(
#[br(args(string_heap, heap_pointer))]
#[bw(args(string_heap))]
EnvSetInstanceObject,
),
#[br(pre_assert(*magic == LayerEntryType::Gathering))]
Gathering(GatheringInstanceObject),
#[br(pre_assert(*magic == LayerEntryType::Treasure))]
Treasure(TreasureInstanceObject),
#[br(pre_assert(*magic == LayerEntryType::PopRange))]
PopRange(PopRangeInstanceObject),
#[br(pre_assert(*magic == LayerEntryType::ExitRange))]
ExitRange(ExitRangeInstanceObject),
#[br(pre_assert(*magic == LayerEntryType::MapRange))]
MapRange(MapRangeInstanceObject),
#[br(pre_assert(*magic == LayerEntryType::EventObject))]
EventObject(EventInstanceObject),
#[br(pre_assert(*magic == LayerEntryType::EnvLocation))]
EnvLocation(
#[br(args(string_heap, heap_pointer))]
#[bw(args(string_heap))]
EnvLocationObject,
),
#[br(pre_assert(*magic == LayerEntryType::EventRange))]
EventRange(EventRangeInstanceObject),
#[br(pre_assert(*magic == LayerEntryType::QuestMarker))]
QuestMarker(QuestMarkerInstanceObject),
#[br(pre_assert(*magic == LayerEntryType::CollisionBox))]
CollisionBox(
#[br(args(string_heap, heap_pointer))]
#[bw(args(string_heap))]
CollisionBoxInstanceObject,
),
#[br(pre_assert(*magic == LayerEntryType::LineVFX))]
LineVFX(LineVFXInstanceObject),
#[br(pre_assert(*magic == LayerEntryType::ClientPath))]
ClientPath(ClientPathInstanceObject),
#[br(pre_assert(*magic == LayerEntryType::ServerPath))]
ServerPath(ServerPathInstanceObject),
#[br(pre_assert(*magic == LayerEntryType::GimmickRange))]
GimmickRange(GimmickRangeInstanceObject),
#[br(pre_assert(*magic == LayerEntryType::TargetMarker))]
TargetMarker(TargetMarkerInstanceObject),
#[br(pre_assert(*magic == LayerEntryType::ChairMarker))]
ChairMarker(ChairMarkerInstanceObject),
#[br(pre_assert(*magic == LayerEntryType::ClickableRange))]
ClickableRange(ClickableRangeInstanceObject),
#[br(pre_assert(*magic == LayerEntryType::PrefetchRange))]
PrefetchRange(PrefetchRangeInstanceObject),
#[br(pre_assert(*magic == LayerEntryType::FateRange))]
FateRange(FateRangeInstanceObject),
#[br(pre_assert(*magic == LayerEntryType::Unk1))]
Unk1(),
#[br(pre_assert(*magic == LayerEntryType::Unk2))]
Unk2(),
#[br(pre_assert(*magic == LayerEntryType::Unk3))]
Unk3(),
#[br(pre_assert(*magic == LayerEntryType::Unk4))]
Unk4(),
#[br(pre_assert(*magic == LayerEntryType::DoorRange))]
DoorRange(DoorRangeInstanceObject),
Unknown(),
}
#[binrw]
#[brw(repr = i32)]
#[derive(Debug, PartialEq)]
pub enum LayerSetReferencedType {
All = 0x0,
Include = 0x1,
Exclude = 0x2,
Undetermined = 0x3,
}
#[binrw]
#[derive(Debug, PartialEq)]
#[br(import(endianness: Endian, data_heap: &StringHeap, string_heap: &StringHeap), stream = r)]
#[bw(import(data_heap: &mut StringHeap, string_heap: &mut StringHeap))]
#[allow(dead_code)] pub struct LayerHeader {
#[br(temp)]
#[bw(ignore)]
heap_pointer: HeapPointer,
pub layer_id: u32,
#[br(args(heap_pointer, string_heap))]
#[bw(args(string_heap))]
pub name: HeapString,
pub(crate) instance_object_offset: i32,
pub(crate) instance_object_count: i32,
#[br(map = read_bool_from::<u8>)]
#[bw(map = write_bool_as::<u8>)]
pub tool_mode_visible: bool,
#[br(map = read_bool_from::<u8>)]
#[bw(map = write_bool_as::<u8>)]
pub tool_mode_read_only: bool,
#[br(map = read_bool_from::<u8>)]
#[bw(map = write_bool_as::<u8>)]
pub is_bush_layer: bool,
#[br(map = read_bool_from::<u8>)]
#[bw(map = write_bool_as::<u8>)]
pub ps3_visible: bool,
#[br(temp)]
#[bw(calc = data_heap.get_free_offset_args(&layer_set_referenced_list))]
pub(crate) layer_set_referenced_list_offset: i32,
#[br(calc = data_heap.read_args(r, endianness, heap_pointer, layer_set_referenced_list_offset))]
#[bw(ignore)]
pub layer_set_referenced_list: LayerSetReferencedList,
pub festival_id: u16,
pub festival_phase_id: u16,
#[br(map = read_bool_from::<u8>)]
#[bw(map = write_bool_as::<u8>)]
pub is_temporary: bool,
#[br(map = read_bool_from::<u8>)]
#[bw(map = write_bool_as::<u8>)]
pub is_housing: bool,
pub version_mask: u16,
#[brw(pad_before = 4)]
pub(crate) ob_set_referenced_list_offset: i32,
pub(crate) ob_set_referenced_list_count: i32,
#[br(calc = data_heap.read_vec_args(r, endianness, string_heap, heap_pointer, ob_set_referenced_list_count as usize, ob_set_referenced_list_offset))]
#[bw(ignore)]
pub object_set_referenced: Vec<ObjectSetReferenced>,
pub(crate) ob_set_enable_referenced_list_offset: i32,
pub(crate) ob_set_enable_referenced_list_count: i32,
#[br(calc = data_heap.read_vec_args(r, endianness, string_heap, heap_pointer, ob_set_enable_referenced_list_count as usize, ob_set_enable_referenced_list_offset))]
#[bw(ignore)]
pub object_set_enable_referenced: Vec<ObjectSetEnableReferenced>,
}
impl LayerHeader {
pub fn has_layer_set(&self, id: u32) -> bool {
match self.layer_set_referenced_list.referenced_type {
LayerSetReferencedType::Include => self
.layer_set_referenced_list
.layer_sets
.iter()
.any(|x| x.layer_set_id == id),
LayerSetReferencedType::Exclude => !self
.layer_set_referenced_list
.layer_sets
.iter()
.any(|x| x.layer_set_id == id),
LayerSetReferencedType::All => true, _ => false, }
}
}
#[binrw]
#[derive(Debug, PartialEq)]
#[allow(dead_code)] pub struct LayerSetReferenced {
pub layer_set_id: u32,
}
#[binrw]
#[br(import(data_heap: &StringHeap), stream = r)]
#[bw(import(data_heap: &mut StringHeap))]
#[derive(Debug, PartialEq)]
pub struct LayerSetReferencedList {
pub referenced_type: LayerSetReferencedType,
#[br(temp)]
#[bw(calc = data_heap.get_free_offset(&layer_sets))]
layer_set_offset: i32,
#[bw(calc = layer_sets.len() as i32)]
pub layer_set_count: i32,
#[br(count = layer_set_count)]
#[bw(ignore)]
pub layer_sets: Vec<LayerSetReferenced>,
}
#[binrw]
#[br(import(string_heap: &StringHeap), stream = r)]
#[bw(import(string_heap: &mut StringHeap))]
#[derive(Debug, PartialEq)]
pub struct ObjectSetReferenced {
#[br(temp)]
#[bw(ignore)]
heap_pointer: HeapPointer,
pub asset_type: LayerEntryType,
pub instance_id: u32,
#[br(args(heap_pointer, string_heap))]
#[bw(args(string_heap))]
pub obsb_path: HeapString,
}
#[binrw]
#[br(import(string_heap: &StringHeap), stream = r)]
#[bw(import(string_heap: &mut StringHeap))]
#[derive(Debug, PartialEq)]
pub struct ObjectSetEnableReferenced {
pub asset_type: LayerEntryType,
pub instance_id: u32,
#[br(map = read_bool_from::<u8>)]
#[bw(map = write_bool_as::<u8>)]
pub ob_set_enable: bool,
#[br(map = read_bool_from::<u8>)]
#[bw(map = write_bool_as::<u8>)]
pub ob_set_emissive_enable: bool,
padding: [u8; 2],
}
#[binrw]
#[derive(Debug, PartialEq)]
#[br(import(string_heap: &StringHeap))]
#[bw(import(string_heap: &mut StringHeap))]
#[allow(dead_code)] pub struct InstanceObject {
#[br(temp)]
#[bw(ignore)]
heap_pointer: HeapPointer,
asset_type: LayerEntryType,
pub instance_id: u32,
#[br(args(heap_pointer, string_heap))]
#[bw(args(string_heap))]
pub name: HeapString,
pub transform: Transformation,
#[br(args(&asset_type, string_heap, heap_pointer))]
#[bw(args(string_heap))]
pub data: LayerEntryData,
}
#[derive(Debug, PartialEq)]
pub struct Layer {
pub header: LayerHeader,
pub objects: Vec<InstanceObject>,
}
impl Layer {
pub(crate) fn read<T: Read + Seek>(
endianness: Endian,
cursor: &mut T,
data_heap: &StringHeap,
string_heap: &StringHeap,
) -> Option<Layer> {
let old_pos = cursor.stream_position().unwrap();
let header =
LayerHeader::read_options(cursor, endianness, (endianness, data_heap, string_heap))
.unwrap();
let mut objects = Vec::new();
{
let mut instance_offsets = vec![0i32; header.instance_object_count as usize];
for i in 0..header.instance_object_count {
instance_offsets[i as usize] =
cursor.read_type_args::<i32>(endianness, ()).unwrap();
}
for i in 0..header.instance_object_count {
cursor
.seek(SeekFrom::Start(
old_pos
+ header.instance_object_offset as u64
+ instance_offsets[i as usize] as u64,
))
.unwrap();
let start = cursor.stream_position().unwrap();
objects
.push(InstanceObject::read_options(cursor, endianness, (string_heap,)).ok()?);
let after_immediate_read = cursor.stream_position().unwrap();
let next_offset = if i + 1 < header.instance_object_count {
old_pos
+ header.instance_object_offset as u64
+ instance_offsets[i as usize + 1] as u64
} else {
old_pos + header.ob_set_referenced_list_offset as u64
};
let expected_size = next_offset as u64 - start;
let actual_size = after_immediate_read - start;
}
}
Some(Layer { header, objects })
}
}