physis/
avfx.rs

1// SPDX-FileCopyrightText: 2024 Joshua Goins <josh@redstrate.com>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use std::io::{Cursor, Seek, SeekFrom};
5
6use crate::ByteSpan;
7use binrw::BinRead;
8use binrw::{BinReaderExt, binread, binrw};
9
10#[binrw]
11#[derive(Debug)]
12#[brw(little)]
13struct AvfxHeader {
14    name: u32,
15    size: u32,
16}
17
18#[binread]
19#[derive(Debug)]
20enum AvfxData {
21    #[brw(magic = b"XFVA")]
22    AvfxBase,
23    #[brw(magic = b"reV\0")]
24    Version,
25    #[brw(magic = b"PFDb")]
26    IsDelayFastParticle,
27    #[brw(magic = b"GFb\0")]
28    IsFitGround,
29    #[brw(magic = b"STb\0")]
30    IsTransformSkip,
31    #[brw(magic = b"HSAb")]
32    IsAllStopOnHide,
33    #[brw(magic = b"CBCb")]
34    CanBeClippedOut,
35    #[brw(magic = b"luCb")]
36    ClipBoxEnabled,
37    #[brw(magic = b"xPBC")]
38    ClipBoxX,
39    #[brw(magic = b"yPBC")]
40    ClipBoxY,
41    #[brw(magic = b"zPBC")]
42    ClipBoxZ,
43    #[brw(magic = b"xSBC")]
44    ClipBoxSizeX,
45    #[brw(magic = b"ySBC")]
46    ClipBoxSizeY,
47    #[brw(magic = b"zSBC")]
48    ClipBoxSizeZ,
49    #[brw(magic = b"sMBZ")]
50    BiasZmaxScale,
51    #[brw(magic = b"dMBZ")]
52    BiasZmaxDistance,
53    #[brw(magic = b"SmCb")]
54    IsCameraSpace,
55    #[brw(magic = b"LEFb")]
56    IsFullEnvLight,
57    #[brw(magic = b"tSOb")]
58    IsClipOwnSetting,
59    #[brw(magic = b"BCN\0")]
60    NearClipBegin,
61    #[brw(magic = b"ECN\0")]
62    NearClipEnd,
63    #[brw(magic = b"BCF\0")]
64    FarClipBegin,
65    #[brw(magic = b"ECF\0")]
66    FarClipEnd,
67    #[brw(magic = b"RFPS")]
68    SoftParticleFadeRange,
69    #[brw(magic = b"OKS\0")]
70    SoftKeyOffset,
71    #[brw(magic = b"yLwD")]
72    DrawLayerType,
73    #[brw(magic = b"TOwD")]
74    DrawOrderType,
75    #[brw(magic = b"TSLD")]
76    DirectionalLightSourceType,
77    #[brw(magic = b"S1LP")]
78    PointLightsType1,
79    #[brw(magic = b"S2LP")]
80    PointLightsType2,
81    #[brw(magic = b"xPvR")]
82    RevisedValuesPosX,
83    #[brw(magic = b"yPvR")]
84    RevisedValuesPosY,
85    #[brw(magic = b"zPvR")]
86    RevisedValuesPosZ,
87    #[brw(magic = b"xRvR")]
88    RevisedValuesRotX,
89    #[brw(magic = b"yRvR")]
90    RevisedValuesRotY,
91    #[brw(magic = b"zRvR")]
92    RevisedValuesRotZ,
93    #[brw(magic = b"xSvR")]
94    RevisedValuesScaleX,
95    #[brw(magic = b"ySvR")]
96    RevisedValuesScaleY,
97    #[brw(magic = b"zSvR")]
98    RevisedValuesScaleZ,
99    #[brw(magic = b"RvR\0")]
100    RevisedValuesColorR,
101    #[brw(magic = b"GvR\0")]
102    RevisedValuesColorG,
103    #[brw(magic = b"BvR\0")]
104    RevisedValuesColorB,
105    #[brw(magic = b"eXFA")]
106    FadeEnabledX,
107    #[brw(magic = b"iXFA")]
108    FadeInnerX,
109    #[brw(magic = b"oXFA")]
110    FadeOuterX,
111    #[brw(magic = b"eYFA")]
112    FadeEnabledY,
113    #[brw(magic = b"iYFA")]
114    FadeInnerY,
115    #[brw(magic = b"oYFA")]
116    FadeOuterY,
117    #[brw(magic = b"eZFA")]
118    FadeEnabledZ,
119    #[brw(magic = b"iZFA")]
120    FadeInnerZ,
121    #[brw(magic = b"oZFA")]
122    FadeOuterZ,
123    #[brw(magic = b"EFGb")]
124    GlobalFogEnabled,
125    #[brw(magic = b"MIFG")]
126    GlobalFogInfluence,
127    #[brw(magic = b"SGAb")]
128    AgsEnabled,
129    #[brw(magic = b"STLb")]
130    LtsEnabled,
131    #[brw(magic = b"nCcS")]
132    NumSchedulers,
133    #[brw(magic = b"nClT")]
134    NumTimelines,
135    #[brw(magic = b"nCmE")]
136    NumEmitters,
137    #[brw(magic = b"nCrP")]
138    NumParticles,
139    #[brw(magic = b"nCfE")]
140    NumEffectors,
141    #[brw(magic = b"nCdB")]
142    NumBinders,
143    #[brw(magic = b"nCxT")]
144    NumTextures,
145    #[brw(magic = b"nCdM")]
146    NumModels,
147    #[brw(magic = b"dhcS")]
148    Scheduler,
149    #[brw(magic = b"nLmT")]
150    Timeline,
151    #[brw(magic = b"timE")]
152    Emitter,
153    #[brw(magic = b"lctP")]
154    Particle,
155    #[brw(magic = b"tcfE")]
156    Effector,
157    #[brw(magic = b"dniB")]
158    Binder,
159    #[brw(magic = b"xeT\0")]
160    Texture,
161    #[brw(magic = b"ldoM")]
162    Model,
163}
164
165#[binread]
166#[derive(Debug)]
167#[brw(little)]
168struct AvfxBlock {
169    #[br(pad_before = 4)]
170    size: u32,
171
172    #[br(seek_before = SeekFrom::Current(-8))]
173    #[br(pad_after = 4)] // skip over size
174    data: AvfxData,
175}
176
177#[derive(Debug)]
178#[allow(dead_code)]
179pub struct Avfx {
180    clip_box: [f32; 3],
181    clip_box_size: [f32; 3],
182    revised_values_position: [f32; 3],
183    revised_values_rotation: [f32; 3],
184    revised_values_scale: [f32; 3],
185    revised_values_color: [f32; 3],
186
187    version: u32,
188    draw_layer_type: u32,
189    draw_order_type: u32,
190    directional_light_source_type: u32,
191    point_lights_type1: u32,
192    point_lights_type2: u32,
193
194    bias_z_max_scale: f32,
195    bias_z_max_distance: f32,
196    near_clip_begin: f32,
197    near_clip_end: f32,
198    fade_inner: [f32; 3],
199    fade_outer: [f32; 3],
200    far_clip_begin: f32,
201    far_clip_end: f32,
202    soft_particle_fade_range: f32,
203    soft_key_offset: f32,
204    global_fog_influence: f32,
205
206    is_delay_fast_particle: bool,
207    is_fit_ground: bool,
208    is_transform_skip: bool,
209    is_all_stop_on_hide: bool,
210    can_be_clipped_out: bool,
211    clip_box_enabled: bool,
212    is_camera_space: bool,
213    is_full_env_light: bool,
214    is_clip_own_setting: bool,
215    fade_enabled_x: bool,
216    fade_enabled_y: bool,
217    fade_enabled_z: bool,
218    global_fog_enabled: bool,
219    lts_enabled: bool,
220    ags_enabled: bool,
221
222    schedulers: Vec<AvfxBlock>,
223    timelines: Vec<AvfxBlock>,
224    emitters: Vec<AvfxBlock>,
225    particles: Vec<AvfxBlock>,
226    effectors: Vec<AvfxBlock>,
227    binders: Vec<AvfxBlock>,
228    textures: Vec<String>,
229    model: Vec<AvfxBlock>,
230}
231
232impl Default for Avfx {
233    fn default() -> Self {
234        Self {
235            clip_box: [0.0; 3],
236            clip_box_size: [0.0; 3],
237            revised_values_position: [0.0; 3],
238            revised_values_rotation: [0.0; 3],
239            revised_values_scale: [0.0; 3],
240            revised_values_color: [0.0; 3],
241            version: 0,
242            draw_layer_type: 0,
243            draw_order_type: 0,
244            directional_light_source_type: 0,
245            point_lights_type1: 0,
246            point_lights_type2: 0,
247            bias_z_max_scale: 0.0,
248            bias_z_max_distance: 0.0,
249            near_clip_begin: 0.0,
250            near_clip_end: 0.0,
251            fade_inner: [0.0; 3],
252            fade_outer: [0.0; 3],
253            far_clip_begin: 0.0,
254            far_clip_end: 0.0,
255            soft_particle_fade_range: 0.0,
256            soft_key_offset: 0.0,
257            global_fog_influence: 0.0,
258            is_delay_fast_particle: false,
259            is_fit_ground: false,
260            is_transform_skip: false,
261            is_all_stop_on_hide: false,
262            can_be_clipped_out: false,
263            clip_box_enabled: false,
264            is_camera_space: false,
265            is_full_env_light: false,
266            is_clip_own_setting: false,
267            fade_enabled_x: false,
268            fade_enabled_y: false,
269            fade_enabled_z: false,
270            global_fog_enabled: false,
271            lts_enabled: false,
272            ags_enabled: false,
273            schedulers: vec![],
274            timelines: vec![],
275            emitters: vec![],
276            particles: vec![],
277            effectors: vec![],
278            binders: vec![],
279            textures: vec![],
280            model: vec![],
281        }
282    }
283}
284
285impl Avfx {
286    /// Reads an existing Avfx file
287    pub fn from_existing(buffer: ByteSpan) -> Option<Self> {
288        let mut cursor = Cursor::new(buffer);
289        let header = AvfxHeader::read(&mut cursor).ok()?;
290
291        let mut avfx = Avfx::default();
292
293        let read_bool = |cursor: &mut Cursor<ByteSpan>| cursor.read_le::<u8>().unwrap() == 1u8;
294
295        let read_uint = |cursor: &mut Cursor<ByteSpan>| cursor.read_le::<u32>().unwrap();
296
297        let read_float = |cursor: &mut Cursor<ByteSpan>| cursor.read_le::<f32>().unwrap();
298
299        while cursor.position() < header.size as u64 {
300            let last_pos = cursor.position();
301            let block = AvfxBlock::read(&mut cursor).unwrap();
302            match block.data {
303                AvfxData::AvfxBase => {}
304                AvfxData::Version => {
305                    avfx.version = read_uint(&mut cursor);
306                }
307                AvfxData::IsDelayFastParticle => {
308                    avfx.is_delay_fast_particle = read_bool(&mut cursor);
309                }
310                AvfxData::IsFitGround => {
311                    avfx.is_fit_ground = read_bool(&mut cursor);
312                }
313                AvfxData::IsTransformSkip => {
314                    avfx.is_transform_skip = read_bool(&mut cursor);
315                }
316                AvfxData::IsAllStopOnHide => {
317                    avfx.is_all_stop_on_hide = read_bool(&mut cursor);
318                }
319                AvfxData::CanBeClippedOut => {
320                    avfx.can_be_clipped_out = read_bool(&mut cursor);
321                }
322                AvfxData::ClipBoxEnabled => {
323                    avfx.clip_box_enabled = read_bool(&mut cursor);
324                }
325                AvfxData::ClipBoxX => {
326                    avfx.clip_box[0] = read_float(&mut cursor);
327                }
328                AvfxData::ClipBoxY => {
329                    avfx.clip_box[1] = read_float(&mut cursor);
330                }
331                AvfxData::ClipBoxZ => {
332                    avfx.clip_box[2] = read_float(&mut cursor);
333                }
334                AvfxData::ClipBoxSizeX => {
335                    avfx.clip_box_size[0] = read_float(&mut cursor);
336                }
337                AvfxData::ClipBoxSizeY => {
338                    avfx.clip_box_size[1] = read_float(&mut cursor);
339                }
340                AvfxData::ClipBoxSizeZ => {
341                    avfx.clip_box_size[2] = read_float(&mut cursor);
342                }
343                AvfxData::BiasZmaxScale => {
344                    avfx.bias_z_max_scale = read_float(&mut cursor);
345                }
346                AvfxData::BiasZmaxDistance => {
347                    avfx.bias_z_max_distance = read_float(&mut cursor);
348                }
349                AvfxData::IsCameraSpace => {
350                    avfx.is_camera_space = read_bool(&mut cursor);
351                }
352                AvfxData::IsFullEnvLight => {
353                    avfx.is_full_env_light = read_bool(&mut cursor);
354                }
355                AvfxData::IsClipOwnSetting => {
356                    avfx.is_clip_own_setting = read_bool(&mut cursor);
357                }
358                AvfxData::NearClipBegin => {
359                    avfx.near_clip_begin = read_float(&mut cursor);
360                }
361                AvfxData::NearClipEnd => {
362                    avfx.near_clip_end = read_float(&mut cursor);
363                }
364                AvfxData::FarClipBegin => {
365                    avfx.far_clip_begin = read_float(&mut cursor);
366                }
367                AvfxData::FarClipEnd => {
368                    avfx.far_clip_end = read_float(&mut cursor);
369                }
370                AvfxData::SoftParticleFadeRange => {
371                    avfx.soft_particle_fade_range = read_float(&mut cursor);
372                }
373                AvfxData::SoftKeyOffset => {
374                    avfx.soft_key_offset = read_float(&mut cursor);
375                }
376                AvfxData::DrawLayerType => {
377                    avfx.draw_layer_type = read_uint(&mut cursor);
378                }
379                AvfxData::DrawOrderType => {
380                    avfx.draw_order_type = read_uint(&mut cursor);
381                }
382                AvfxData::DirectionalLightSourceType => {
383                    avfx.directional_light_source_type = read_uint(&mut cursor);
384                }
385                AvfxData::PointLightsType1 => {
386                    avfx.point_lights_type1 = read_uint(&mut cursor);
387                }
388                AvfxData::PointLightsType2 => {
389                    avfx.point_lights_type2 = read_uint(&mut cursor);
390                }
391                AvfxData::RevisedValuesPosX => {
392                    avfx.revised_values_position[0] = read_float(&mut cursor);
393                }
394                AvfxData::RevisedValuesPosY => {
395                    avfx.revised_values_position[1] = read_float(&mut cursor);
396                }
397                AvfxData::RevisedValuesPosZ => {
398                    avfx.revised_values_position[2] = read_float(&mut cursor);
399                }
400                AvfxData::RevisedValuesRotX => {
401                    avfx.revised_values_rotation[0] = read_float(&mut cursor);
402                }
403                AvfxData::RevisedValuesRotY => {
404                    avfx.revised_values_rotation[1] = read_float(&mut cursor);
405                }
406                AvfxData::RevisedValuesRotZ => {
407                    avfx.revised_values_rotation[2] = read_float(&mut cursor);
408                }
409                AvfxData::RevisedValuesScaleX => {
410                    avfx.revised_values_scale[0] = read_float(&mut cursor);
411                }
412                AvfxData::RevisedValuesScaleY => {
413                    avfx.revised_values_scale[1] = read_float(&mut cursor);
414                }
415                AvfxData::RevisedValuesScaleZ => {
416                    avfx.revised_values_scale[2] = read_float(&mut cursor);
417                }
418                AvfxData::RevisedValuesColorR => {
419                    avfx.revised_values_color[0] = read_float(&mut cursor);
420                }
421                AvfxData::RevisedValuesColorG => {
422                    avfx.revised_values_color[1] = read_float(&mut cursor);
423                }
424                AvfxData::RevisedValuesColorB => {
425                    avfx.revised_values_color[2] = read_float(&mut cursor);
426                }
427                AvfxData::FadeEnabledX => {
428                    avfx.fade_enabled_x = read_bool(&mut cursor);
429                }
430                AvfxData::FadeInnerX => {
431                    avfx.fade_inner[0] = read_float(&mut cursor);
432                }
433                AvfxData::FadeOuterX => {
434                    avfx.fade_outer[0] = read_float(&mut cursor);
435                }
436                AvfxData::FadeEnabledY => {
437                    avfx.fade_enabled_y = read_bool(&mut cursor);
438                }
439                AvfxData::FadeInnerY => {
440                    avfx.fade_inner[1] = read_float(&mut cursor);
441                }
442                AvfxData::FadeOuterY => {
443                    avfx.fade_outer[1] = read_float(&mut cursor);
444                }
445                AvfxData::FadeEnabledZ => {
446                    avfx.fade_enabled_z = read_bool(&mut cursor);
447                }
448                AvfxData::FadeInnerZ => {
449                    avfx.fade_inner[2] = read_float(&mut cursor);
450                }
451                AvfxData::FadeOuterZ => {
452                    avfx.fade_outer[2] = read_float(&mut cursor);
453                }
454                AvfxData::GlobalFogEnabled => {
455                    avfx.global_fog_enabled = read_bool(&mut cursor);
456                }
457                AvfxData::GlobalFogInfluence => {
458                    avfx.global_fog_influence = read_float(&mut cursor);
459                }
460                AvfxData::LtsEnabled => {
461                    avfx.lts_enabled = read_bool(&mut cursor);
462                }
463                AvfxData::AgsEnabled => {
464                    avfx.ags_enabled = read_bool(&mut cursor);
465                }
466                AvfxData::NumSchedulers => {
467                    todo!()
468                }
469                AvfxData::NumTimelines => {
470                    todo!()
471                }
472                AvfxData::NumEmitters => {
473                    todo!()
474                }
475                AvfxData::NumParticles => {
476                    todo!()
477                }
478                AvfxData::NumEffectors => {
479                    todo!()
480                }
481                AvfxData::NumBinders => {
482                    todo!()
483                }
484                AvfxData::NumTextures => {
485                    todo!()
486                }
487                AvfxData::NumModels => {
488                    todo!()
489                }
490                AvfxData::Scheduler => {
491                    todo!()
492                }
493                AvfxData::Timeline => {
494                    todo!()
495                }
496                AvfxData::Emitter => {
497                    todo!()
498                }
499                AvfxData::Particle => {
500                    todo!()
501                }
502                AvfxData::Effector => {
503                    todo!()
504                }
505                AvfxData::Binder => {
506                    todo!()
507                }
508                AvfxData::Texture => {
509                    todo!()
510                }
511                AvfxData::Model => {
512                    todo!()
513                }
514            }
515            let new_pos = cursor.position();
516            let read_bytes = (new_pos - last_pos) - 8;
517            let padding = block.size as u64 - read_bytes;
518            cursor.seek(SeekFrom::Current(padding as i64)).ok()?;
519        }
520
521        Some(avfx)
522    }
523}