2023-08-06 08:25:04 -04:00
|
|
|
// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
|
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
2024-04-20 13:17:11 -04:00
|
|
|
#![allow(clippy::unnecessary_fallible_conversions)] // This wrongly trips on binrw code
|
|
|
|
|
2022-08-16 11:52:07 -04:00
|
|
|
use std::io::Cursor;
|
2022-08-11 15:40:14 -04:00
|
|
|
|
2024-04-30 19:43:47 -04:00
|
|
|
use crate::common_file_operations::{Half1, Half2, Half3};
|
2024-05-18 09:42:07 -04:00
|
|
|
use crate::ByteSpan;
|
2024-11-02 19:30:31 -04:00
|
|
|
use binrw::{binread, binrw, BinRead, BinResult};
|
|
|
|
use crate::mtrl::ColorDyeTable::{DawntrailColorDyeTable, LegacyColorDyeTable, OpaqueColorDyeTable};
|
|
|
|
use crate::mtrl::ColorTable::{DawntrailColorTable, LegacyColorTable, OpaqueColorTable};
|
2023-08-06 08:25:04 -04:00
|
|
|
|
2022-10-13 17:11:03 -04:00
|
|
|
#[binrw]
|
2022-08-11 15:40:14 -04:00
|
|
|
#[derive(Debug)]
|
2022-09-15 16:26:31 -04:00
|
|
|
#[allow(dead_code)]
|
2022-08-11 15:40:14 -04:00
|
|
|
struct MaterialFileHeader {
|
|
|
|
version: u32,
|
|
|
|
file_size: u16,
|
|
|
|
data_set_size: u16,
|
|
|
|
string_table_size: u16,
|
|
|
|
shader_package_name_offset: u16,
|
|
|
|
texture_count: u8,
|
|
|
|
uv_set_count: u8,
|
|
|
|
color_set_count: u8,
|
2022-08-16 11:52:07 -04:00
|
|
|
additional_data_size: u8,
|
2022-08-11 15:40:14 -04:00
|
|
|
}
|
|
|
|
|
2022-10-13 17:11:03 -04:00
|
|
|
#[binrw]
|
2022-08-11 15:40:14 -04:00
|
|
|
#[derive(Debug)]
|
|
|
|
struct MaterialHeader {
|
|
|
|
shader_value_list_size: u16,
|
|
|
|
shader_key_count: u16,
|
|
|
|
constant_count: u16,
|
2022-08-16 11:52:07 -04:00
|
|
|
sampler_count: u16,
|
2024-11-02 19:30:31 -04:00
|
|
|
flags: u32,
|
2022-08-11 15:40:14 -04:00
|
|
|
}
|
|
|
|
|
2022-10-13 17:11:03 -04:00
|
|
|
#[binrw]
|
2022-08-11 15:40:14 -04:00
|
|
|
#[derive(Debug)]
|
2022-09-15 16:26:31 -04:00
|
|
|
#[allow(dead_code)]
|
2022-08-11 15:40:14 -04:00
|
|
|
struct ColorSet {
|
|
|
|
name_offset: u16,
|
2024-11-02 19:30:31 -04:00
|
|
|
index: u16,
|
2022-08-11 15:40:14 -04:00
|
|
|
}
|
|
|
|
|
2024-04-30 19:43:47 -04:00
|
|
|
#[binread]
|
2024-05-04 14:19:55 -04:00
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
#[repr(C)]
|
2024-04-30 19:43:47 -04:00
|
|
|
#[allow(dead_code)]
|
2024-11-02 19:30:31 -04:00
|
|
|
pub struct DawntrailColorTableRow {
|
|
|
|
#[br(map = |x: Half3| { [x.r.to_f32(), x.g.to_f32(), x.b.to_f32()] })]
|
|
|
|
pub diffuse_color: [f32; 3],
|
|
|
|
|
|
|
|
#[br(map = |x: Half1| { x.value.to_f32() })]
|
|
|
|
pub unknown1: f32,
|
|
|
|
|
|
|
|
#[br(map = |x: Half3| { [x.r.to_f32(), x.g.to_f32(), x.b.to_f32()] })]
|
|
|
|
pub specular_color: [f32; 3],
|
|
|
|
|
|
|
|
#[br(map = |x: Half1| { x.value.to_f32() })]
|
|
|
|
pub unknown2: f32,
|
|
|
|
|
|
|
|
#[br(map = |x: Half3| { [x.r.to_f32(), x.g.to_f32(), x.b.to_f32()] })]
|
|
|
|
pub emissive_color: [f32; 3],
|
|
|
|
|
|
|
|
#[br(map = |x: Half1| { x.value.to_f32() })]
|
|
|
|
pub unknown3: f32,
|
|
|
|
|
|
|
|
#[br(map = |x: Half1| { x.value.to_f32() })]
|
|
|
|
pub sheen_rate: f32,
|
|
|
|
|
|
|
|
#[br(map = |x: Half1| { x.value.to_f32() })]
|
|
|
|
pub sheen_tint: f32,
|
|
|
|
|
|
|
|
#[br(map = |x: Half1| { x.value.to_f32() })]
|
|
|
|
pub sheen_aperture: f32,
|
|
|
|
|
|
|
|
#[br(map = |x: Half1| { x.value.to_f32() })]
|
|
|
|
pub unknown4: f32,
|
|
|
|
|
|
|
|
#[br(map = |x: Half1| { x.value.to_f32() })]
|
|
|
|
pub roughness: f32,
|
|
|
|
|
|
|
|
#[br(map = |x: Half1| { x.value.to_f32() })]
|
|
|
|
pub unknown5: f32,
|
|
|
|
|
|
|
|
#[br(map = |x: Half1| { x.value.to_f32() })]
|
|
|
|
pub metalness: f32,
|
|
|
|
|
|
|
|
#[br(map = |x: Half1| { x.value.to_f32() })]
|
|
|
|
pub anisotropy: f32,
|
|
|
|
|
|
|
|
#[br(map = |x: Half1| { x.value.to_f32() })]
|
|
|
|
pub unknown6: f32,
|
|
|
|
|
|
|
|
#[br(map = |x: Half1| { x.value.to_f32() })]
|
|
|
|
pub sphere_mask: f32,
|
|
|
|
|
|
|
|
#[br(map = |x: Half1| { x.value.to_f32() })]
|
|
|
|
pub unknown7: f32,
|
|
|
|
|
|
|
|
#[br(map = |x: Half1| { x.value.to_f32() })]
|
|
|
|
pub unknown8: f32,
|
|
|
|
|
|
|
|
pub shader_index: u16,
|
|
|
|
|
|
|
|
pub tile_set: u16,
|
|
|
|
|
|
|
|
#[br(map = |x: Half1| { x.value.to_f32() })]
|
|
|
|
pub tile_alpha: f32,
|
|
|
|
|
|
|
|
pub sphere_index: u16,
|
|
|
|
|
|
|
|
#[br(map = |x: Half2| { [x.x.to_f32(), x.y.to_f32()] })]
|
|
|
|
pub material_repeat: [f32; 2],
|
|
|
|
|
|
|
|
#[br(map = |x: Half2| { [x.x.to_f32(), x.y.to_f32()] })]
|
|
|
|
pub material_skew: [f32; 2],
|
|
|
|
}
|
|
|
|
|
|
|
|
#[binread]
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
#[repr(C)]
|
|
|
|
#[allow(dead_code)]
|
|
|
|
pub struct LegacyColorTableRow {
|
2024-05-04 14:19:55 -04:00
|
|
|
#[br(map = |x: Half3| { [x.r.to_f32(), x.g.to_f32(), x.b.to_f32()] })]
|
|
|
|
pub diffuse_color: [f32; 3],
|
|
|
|
|
|
|
|
#[br(map = |x: Half1| { x.value.to_f32() })]
|
|
|
|
pub specular_strength: f32,
|
|
|
|
|
|
|
|
#[br(map = |x: Half3| { [x.r.to_f32(), x.g.to_f32(), x.b.to_f32()] })]
|
|
|
|
pub specular_color: [f32; 3],
|
|
|
|
|
|
|
|
#[br(map = |x: Half1| { x.value.to_f32() })]
|
|
|
|
pub gloss_strength: f32,
|
|
|
|
|
|
|
|
#[br(map = |x: Half3| { [x.r.to_f32(), x.g.to_f32(), x.b.to_f32()] })]
|
|
|
|
pub emissive_color: [f32; 3],
|
|
|
|
|
|
|
|
pub tile_set: u16,
|
|
|
|
|
|
|
|
#[br(map = |x: Half2| { [x.x.to_f32(), x.y.to_f32()] })]
|
|
|
|
pub material_repeat: [f32; 2],
|
|
|
|
|
|
|
|
#[br(map = |x: Half2| { [x.x.to_f32(), x.y.to_f32()] })]
|
|
|
|
pub material_skew: [f32; 2],
|
2024-04-30 19:43:47 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[binread]
|
2022-08-11 15:40:14 -04:00
|
|
|
#[derive(Debug)]
|
2022-09-15 16:26:31 -04:00
|
|
|
#[allow(dead_code)]
|
2024-11-02 19:30:31 -04:00
|
|
|
pub struct LegacyColorTableData {
|
|
|
|
#[br(count = 16)]
|
|
|
|
pub rows: Vec<LegacyColorTableRow>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[binread]
|
|
|
|
#[derive(Debug)]
|
|
|
|
#[allow(dead_code)]
|
|
|
|
pub struct DawntrailColorTableData {
|
|
|
|
#[br(count = 32)]
|
|
|
|
pub rows: Vec<DawntrailColorTableRow>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[binread]
|
|
|
|
#[derive(Debug)]
|
|
|
|
#[allow(dead_code)]
|
|
|
|
pub struct OpaqueColorTableData {
|
|
|
|
// TODO: Support
|
|
|
|
}
|
|
|
|
|
|
|
|
#[binread]
|
|
|
|
#[derive(Debug)]
|
|
|
|
#[allow(dead_code)]
|
|
|
|
pub enum ColorTable {
|
|
|
|
LegacyColorTable(LegacyColorTableData),
|
|
|
|
DawntrailColorTable(DawntrailColorTableData),
|
|
|
|
OpaqueColorTable(OpaqueColorTableData)
|
2022-08-11 15:40:14 -04:00
|
|
|
}
|
|
|
|
|
2024-04-30 19:48:32 -04:00
|
|
|
#[binread]
|
|
|
|
#[derive(Debug)]
|
|
|
|
#[allow(dead_code)]
|
2024-11-02 19:30:31 -04:00
|
|
|
pub struct LegacyColorDyeTableRow {
|
2024-04-30 19:48:32 -04:00
|
|
|
#[br(temp)]
|
|
|
|
data: u16,
|
|
|
|
|
|
|
|
#[br(calc = data >> 5)]
|
2024-05-04 14:19:55 -04:00
|
|
|
pub template: u16,
|
2024-04-30 19:48:32 -04:00
|
|
|
|
|
|
|
#[br(calc = (data & 0x01) != 0)]
|
2024-05-04 14:19:55 -04:00
|
|
|
pub diffuse: bool,
|
2024-04-30 19:48:32 -04:00
|
|
|
|
|
|
|
#[br(calc = (data & 0x02) != 0)]
|
2024-05-04 14:19:55 -04:00
|
|
|
pub specular: bool,
|
2024-04-30 19:48:32 -04:00
|
|
|
|
|
|
|
#[br(calc = (data & 0x04) != 0)]
|
2024-05-04 14:19:55 -04:00
|
|
|
pub emissive: bool,
|
2024-04-30 19:48:32 -04:00
|
|
|
|
|
|
|
#[br(calc = (data & 0x08) != 0)]
|
2024-05-04 14:19:55 -04:00
|
|
|
pub gloss: bool,
|
2024-04-30 19:48:32 -04:00
|
|
|
|
|
|
|
#[br(calc = (data & 0x10) != 0)]
|
2024-05-04 14:19:55 -04:00
|
|
|
pub specular_strength: bool,
|
2024-04-30 19:48:32 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[binread]
|
2022-08-11 15:40:14 -04:00
|
|
|
#[derive(Debug)]
|
2022-09-15 16:26:31 -04:00
|
|
|
#[allow(dead_code)]
|
2024-11-02 19:30:31 -04:00
|
|
|
pub struct DawntrailColorDyeTableRow {
|
|
|
|
#[br(temp)]
|
|
|
|
data: u32,
|
|
|
|
|
|
|
|
#[br(calc = ((data >> 16) & 0x7FF) as u16)]
|
|
|
|
pub template: u16,
|
|
|
|
|
|
|
|
#[br(calc = ((data >> 27) & 0x3) as u8)]
|
|
|
|
pub channel: u8,
|
|
|
|
|
|
|
|
#[br(calc = (data & 0x0001) != 0)]
|
|
|
|
pub diffuse: bool,
|
|
|
|
|
|
|
|
#[br(calc = (data & 0x0002) != 0)]
|
|
|
|
pub specular: bool,
|
|
|
|
|
|
|
|
#[br(calc = (data & 0x0004) != 0)]
|
|
|
|
pub emissive: bool,
|
|
|
|
|
|
|
|
#[br(calc = (data & 0x0008) != 0)]
|
|
|
|
pub scalar3: bool,
|
|
|
|
|
|
|
|
#[br(calc = (data & 0x0010) != 0)]
|
|
|
|
pub metalness: bool,
|
|
|
|
|
|
|
|
#[br(calc = (data & 0x0020) != 0)]
|
|
|
|
pub roughness: bool,
|
|
|
|
|
|
|
|
#[br(calc = (data & 0x0040) != 0)]
|
|
|
|
pub sheen_rate: bool,
|
|
|
|
|
|
|
|
#[br(calc = (data & 0x0080) != 0)]
|
|
|
|
pub sheen_tint_rate: bool,
|
|
|
|
|
|
|
|
#[br(calc = (data & 0x0100) != 0)]
|
|
|
|
pub sheen_aperture: bool,
|
|
|
|
|
|
|
|
#[br(calc = (data & 0x0200) != 0)]
|
|
|
|
pub anisotropy: bool,
|
|
|
|
|
|
|
|
#[br(calc = (data & 0x0400) != 0)]
|
|
|
|
pub sphere_map_index: bool,
|
|
|
|
|
|
|
|
#[br(calc = (data & 0x0800) != 0)]
|
|
|
|
pub sphere_map_mask: bool,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[binread]
|
|
|
|
#[derive(Debug)]
|
|
|
|
#[allow(dead_code)]
|
|
|
|
pub struct LegacyColorDyeTableData {
|
2022-08-11 15:40:14 -04:00
|
|
|
#[br(count = 16)]
|
2024-11-02 19:30:31 -04:00
|
|
|
pub rows: Vec<LegacyColorDyeTableRow>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[binread]
|
|
|
|
#[derive(Debug)]
|
|
|
|
#[allow(dead_code)]
|
|
|
|
pub struct DawntrailColorDyeTableData {
|
|
|
|
#[br(count = 32)]
|
|
|
|
pub rows: Vec<DawntrailColorDyeTableRow>,
|
|
|
|
}
|
|
|
|
|
|
|
|
#[binread]
|
|
|
|
#[derive(Debug)]
|
|
|
|
#[allow(dead_code)]
|
|
|
|
pub struct OpaqueColorDyeTableData {
|
|
|
|
// TODO: implement
|
|
|
|
}
|
|
|
|
|
|
|
|
#[binread]
|
|
|
|
#[derive(Debug)]
|
|
|
|
#[allow(dead_code)]
|
|
|
|
pub enum ColorDyeTable {
|
|
|
|
LegacyColorDyeTable(LegacyColorDyeTableData),
|
|
|
|
DawntrailColorDyeTable(DawntrailColorDyeTableData),
|
|
|
|
OpaqueColorDyeTable(OpaqueColorDyeTableData)
|
2022-08-11 15:40:14 -04:00
|
|
|
}
|
|
|
|
|
2022-10-13 17:11:03 -04:00
|
|
|
#[binrw]
|
2024-04-26 22:40:42 -04:00
|
|
|
#[derive(Debug, Clone, Copy)]
|
2024-04-26 22:36:40 -04:00
|
|
|
#[repr(C)]
|
2022-09-15 16:26:31 -04:00
|
|
|
#[allow(dead_code)]
|
2024-01-29 15:07:59 -05:00
|
|
|
pub struct ShaderKey {
|
|
|
|
pub category: u32,
|
|
|
|
pub value: u32,
|
2022-08-11 15:40:14 -04:00
|
|
|
}
|
|
|
|
|
2022-10-13 17:11:03 -04:00
|
|
|
#[binrw]
|
2024-04-27 11:53:37 -04:00
|
|
|
#[derive(Debug, Clone, Copy)]
|
2022-09-15 16:26:31 -04:00
|
|
|
#[allow(dead_code)]
|
2024-04-27 12:58:36 -04:00
|
|
|
pub struct ConstantStruct {
|
2022-08-11 15:40:14 -04:00
|
|
|
constant_id: u32,
|
|
|
|
value_offset: u16,
|
2022-08-16 11:52:07 -04:00
|
|
|
value_size: u16,
|
2022-08-11 15:40:14 -04:00
|
|
|
}
|
|
|
|
|
2024-04-27 18:43:49 -04:00
|
|
|
#[derive(Debug, Clone)]
|
2024-04-27 12:58:36 -04:00
|
|
|
#[repr(C)]
|
|
|
|
#[allow(dead_code)]
|
|
|
|
pub struct Constant {
|
|
|
|
id: u32,
|
2024-04-27 18:43:49 -04:00
|
|
|
num_values: u32,
|
2024-05-18 09:42:07 -04:00
|
|
|
values: [f32; 4],
|
2024-04-27 12:58:36 -04:00
|
|
|
}
|
|
|
|
|
2024-01-29 15:07:59 -05:00
|
|
|
// from https://github.com/NotAdam/Lumina/blob/master/src/Lumina/Data/Parsing/MtrlStructs.cs
|
|
|
|
#[binrw]
|
2024-04-27 11:53:37 -04:00
|
|
|
#[repr(u8)]
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
2024-04-27 12:15:54 -04:00
|
|
|
pub enum TextureUsage {
|
2024-01-29 15:07:59 -05:00
|
|
|
#[brw(magic = 0x88408C04u32)]
|
|
|
|
Sampler,
|
|
|
|
#[brw(magic = 0x213CB439u32)]
|
|
|
|
Sampler0,
|
|
|
|
#[brw(magic = 0x563B84AFu32)]
|
|
|
|
Sampler1,
|
|
|
|
#[brw(magic = 0xFEA0F3D2u32)]
|
|
|
|
SamplerCatchlight,
|
|
|
|
#[brw(magic = 0x1E6FEF9Cu32)]
|
|
|
|
SamplerColorMap0,
|
|
|
|
#[brw(magic = 0x6968DF0Au32)]
|
|
|
|
SamplerColorMap1,
|
|
|
|
#[brw(magic = 0x115306BEu32)]
|
|
|
|
SamplerDiffuse,
|
|
|
|
#[brw(magic = 0xF8D7957Au32)]
|
|
|
|
SamplerEnvMap,
|
|
|
|
#[brw(magic = 0x8A4E82B6u32)]
|
|
|
|
SamplerMask,
|
|
|
|
#[brw(magic = 0x0C5EC1F1u32)]
|
|
|
|
SamplerNormal,
|
|
|
|
#[brw(magic = 0xAAB4D9E9u32)]
|
|
|
|
SamplerNormalMap0,
|
|
|
|
#[brw(magic = 0xDDB3E97Fu32)]
|
|
|
|
SamplerNormalMap1,
|
|
|
|
#[brw(magic = 0x87F6474Du32)]
|
|
|
|
SamplerReflection,
|
|
|
|
#[brw(magic = 0x2B99E025u32)]
|
|
|
|
SamplerSpecular,
|
|
|
|
#[brw(magic = 0x1BBC2F12u32)]
|
|
|
|
SamplerSpecularMap0,
|
|
|
|
#[brw(magic = 0x6CBB1F84u32)]
|
|
|
|
SamplerSpecularMap1,
|
|
|
|
#[brw(magic = 0xE6321AFCu32)]
|
|
|
|
SamplerWaveMap,
|
|
|
|
#[brw(magic = 0x574E22D6u32)]
|
|
|
|
SamplerWaveletMap0,
|
|
|
|
#[brw(magic = 0x20491240u32)]
|
|
|
|
SamplerWaveletMap1,
|
|
|
|
#[brw(magic = 0x95E1F64Du32)]
|
2024-04-17 20:23:44 -04:00
|
|
|
SamplerWhitecapMap,
|
|
|
|
|
|
|
|
#[brw(magic = 0x565f8fd8u32)]
|
2024-04-20 13:18:03 -04:00
|
|
|
UnknownDawntrail1,
|
2024-06-28 06:06:23 -04:00
|
|
|
|
|
|
|
#[brw(magic = 0xe5338c17u32)]
|
|
|
|
UnknownDawntrail2,
|
2024-01-29 15:07:59 -05:00
|
|
|
}
|
|
|
|
|
2022-10-13 17:11:03 -04:00
|
|
|
#[binrw]
|
2024-04-27 11:53:37 -04:00
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
|
|
#[repr(C)]
|
2022-09-15 16:26:31 -04:00
|
|
|
#[allow(dead_code)]
|
2024-04-27 12:15:54 -04:00
|
|
|
pub struct Sampler {
|
2024-01-29 15:07:59 -05:00
|
|
|
texture_usage: TextureUsage,
|
2022-08-11 15:40:14 -04:00
|
|
|
flags: u32, // TODO: unknown
|
|
|
|
texture_index: u8,
|
2024-05-04 14:19:55 -04:00
|
|
|
unknown1: u8,
|
|
|
|
unknown2: u8,
|
2024-05-18 09:42:07 -04:00
|
|
|
unknown3: u8,
|
2022-08-11 15:40:14 -04:00
|
|
|
}
|
|
|
|
|
2024-11-02 19:30:31 -04:00
|
|
|
#[binrw::parser(reader, endian)]
|
|
|
|
fn parse_color_table(table_dimension_logs: u8) -> BinResult<Option<ColorTable>> {
|
|
|
|
Ok(Some(match table_dimension_logs {
|
|
|
|
0 | 0x42 => LegacyColorTable(LegacyColorTableData::read_options(reader, endian, ())?),
|
|
|
|
0x53 => DawntrailColorTable(DawntrailColorTableData::read_options(reader, endian, ())?),
|
|
|
|
_ => OpaqueColorTable(OpaqueColorTableData::read_options(reader, endian, ())?)
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
|
|
|
#[binrw::parser(reader, endian)]
|
|
|
|
fn parse_color_dye_table(table_dimension_logs: u8) -> BinResult<Option<ColorDyeTable>> {
|
|
|
|
Ok(Some(match table_dimension_logs {
|
|
|
|
0 => LegacyColorDyeTable(LegacyColorDyeTableData::read_options(reader, endian, ())?),
|
|
|
|
0x50...0x5F => DawntrailColorDyeTable(DawntrailColorDyeTableData::read_options(reader, endian, ())?),
|
|
|
|
_ => OpaqueColorDyeTable(OpaqueColorDyeTableData::read_options(reader, endian, ())?)
|
|
|
|
}))
|
|
|
|
}
|
|
|
|
|
2022-10-13 17:11:03 -04:00
|
|
|
#[binrw]
|
2022-08-11 15:40:14 -04:00
|
|
|
#[derive(Debug)]
|
2022-09-15 16:26:31 -04:00
|
|
|
#[allow(dead_code)]
|
2022-10-13 16:03:46 -04:00
|
|
|
#[br(little)]
|
2022-08-11 15:40:14 -04:00
|
|
|
struct MaterialData {
|
|
|
|
file_header: MaterialFileHeader,
|
|
|
|
|
|
|
|
#[br(count = file_header.texture_count)]
|
|
|
|
offsets: Vec<u32>,
|
|
|
|
|
|
|
|
#[br(count = file_header.uv_set_count)]
|
|
|
|
uv_color_sets: Vec<ColorSet>,
|
|
|
|
|
|
|
|
#[br(count = file_header.color_set_count)]
|
|
|
|
color_sets: Vec<ColorSet>,
|
|
|
|
|
|
|
|
#[br(count = file_header.string_table_size)]
|
|
|
|
strings: Vec<u8>,
|
|
|
|
|
2024-11-02 19:30:31 -04:00
|
|
|
#[br(count = file_header.additional_data_size)]
|
|
|
|
#[br(pad_size_to = 4)]
|
|
|
|
#[br(map = |x: Vec<u8>| u32::from_le_bytes(x[0..4].try_into().unwrap()))]
|
|
|
|
table_flags: u32,
|
|
|
|
|
|
|
|
#[br(calc = (table_flags & 0x4) != 0)]
|
|
|
|
#[bw(ignore)]
|
|
|
|
has_table: bool,
|
|
|
|
|
|
|
|
#[br(calc = (table_flags & 0x8) != 0)]
|
|
|
|
#[bw(ignore)]
|
|
|
|
has_dye_table: bool,
|
|
|
|
|
|
|
|
#[br(calc = ((table_flags >> 4) & 0xF) as u8)]
|
|
|
|
#[bw(ignore)]
|
|
|
|
table_width_log: u8,
|
|
|
|
|
|
|
|
#[br(calc = ((table_flags >> 8) & 0xF) as u8)]
|
|
|
|
#[bw(ignore)]
|
|
|
|
table_height_log: u8,
|
|
|
|
|
|
|
|
#[br(calc = (table_flags >> 4) as u8)]
|
|
|
|
#[bw(ignore)]
|
|
|
|
table_dimension_logs: u8,
|
|
|
|
|
|
|
|
#[br(calc = !has_table || table_width_log != 0 && table_height_log != 0)]
|
|
|
|
#[bw(ignore)]
|
|
|
|
is_dawntrail: bool,
|
|
|
|
|
|
|
|
#[br(if(has_table))]
|
|
|
|
#[br(parse_with = parse_color_table)]
|
|
|
|
#[br(args(table_dimension_logs))]
|
2024-04-30 19:43:47 -04:00
|
|
|
#[bw(ignore)]
|
2024-05-04 14:19:55 -04:00
|
|
|
color_table: Option<ColorTable>,
|
2022-08-11 15:40:14 -04:00
|
|
|
|
2024-11-02 19:30:31 -04:00
|
|
|
#[br(if(has_dye_table))]
|
|
|
|
#[br(parse_with = parse_color_dye_table)]
|
|
|
|
#[br(args(table_dimension_logs))]
|
2024-04-30 19:43:47 -04:00
|
|
|
#[bw(ignore)]
|
2024-05-04 14:19:55 -04:00
|
|
|
color_dye_table: Option<ColorDyeTable>,
|
2022-08-11 15:40:14 -04:00
|
|
|
|
|
|
|
header: MaterialHeader,
|
|
|
|
|
|
|
|
#[br(count = header.shader_key_count)]
|
2024-04-27 12:15:54 -04:00
|
|
|
shader_keys: Vec<ShaderKey>,
|
2022-08-11 15:40:14 -04:00
|
|
|
#[br(count = header.constant_count)]
|
2024-04-27 12:58:36 -04:00
|
|
|
constants: Vec<ConstantStruct>,
|
2022-08-11 15:40:14 -04:00
|
|
|
#[br(count = header.sampler_count)]
|
2024-04-27 12:15:54 -04:00
|
|
|
samplers: Vec<Sampler>,
|
2022-08-11 15:40:14 -04:00
|
|
|
#[br(count = header.shader_value_list_size / 4)]
|
2024-04-27 12:15:54 -04:00
|
|
|
shader_values: Vec<f32>,
|
2022-08-11 15:40:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub struct Material {
|
2024-01-29 15:07:59 -05:00
|
|
|
pub shader_package_name: String,
|
2022-08-16 11:52:07 -04:00
|
|
|
pub texture_paths: Vec<String>,
|
2024-04-20 13:18:03 -04:00
|
|
|
pub shader_keys: Vec<ShaderKey>,
|
2024-04-27 12:15:54 -04:00
|
|
|
pub constants: Vec<Constant>,
|
|
|
|
pub samplers: Vec<Sampler>,
|
2024-04-30 19:51:46 -04:00
|
|
|
pub color_table: Option<ColorTable>,
|
2024-05-18 09:42:07 -04:00
|
|
|
pub color_dye_table: Option<ColorDyeTable>,
|
2022-08-11 15:40:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
impl Material {
|
2023-10-13 16:16:04 -04:00
|
|
|
pub fn from_existing(buffer: ByteSpan) -> Option<Material> {
|
2022-08-11 15:40:14 -04:00
|
|
|
let mut cursor = Cursor::new(buffer);
|
2024-06-29 09:33:48 -04:00
|
|
|
let mat_data = MaterialData::read(&mut cursor).ok()?;
|
2024-04-20 13:18:03 -04:00
|
|
|
|
2022-08-11 15:40:14 -04:00
|
|
|
let mut texture_paths = vec![];
|
|
|
|
|
|
|
|
let mut offset = 0;
|
|
|
|
for _ in 0..mat_data.file_header.texture_count {
|
|
|
|
let mut string = String::new();
|
|
|
|
|
2024-04-20 13:17:11 -04:00
|
|
|
let mut next_char = mat_data.strings[offset] as char;
|
2022-08-11 15:40:14 -04:00
|
|
|
while next_char != '\0' {
|
|
|
|
string.push(next_char);
|
|
|
|
offset += 1;
|
2024-04-20 13:17:11 -04:00
|
|
|
next_char = mat_data.strings[offset] as char;
|
2022-08-11 15:40:14 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
texture_paths.push(string);
|
|
|
|
|
|
|
|
offset += 1;
|
|
|
|
}
|
|
|
|
|
2024-01-29 15:07:59 -05:00
|
|
|
// TODO: move to reusable function
|
|
|
|
let mut shader_package_name = String::new();
|
|
|
|
|
|
|
|
offset = mat_data.file_header.shader_package_name_offset as usize;
|
|
|
|
|
|
|
|
let mut next_char = mat_data.strings[offset] as char;
|
|
|
|
while next_char != '\0' {
|
|
|
|
shader_package_name.push(next_char);
|
|
|
|
offset += 1;
|
2024-04-20 13:17:11 -04:00
|
|
|
next_char = mat_data.strings[offset] as char;
|
2024-01-29 15:07:59 -05:00
|
|
|
}
|
|
|
|
|
2024-04-27 12:58:36 -04:00
|
|
|
let mut constants = Vec::new();
|
|
|
|
for constant in mat_data.constants {
|
2024-04-27 18:43:49 -04:00
|
|
|
let mut values: [f32; 4] = [0.0; 4];
|
|
|
|
|
|
|
|
// TODO: use mem::size_of
|
|
|
|
let num_floats = constant.value_size / 4;
|
|
|
|
for i in 0..num_floats as usize {
|
|
|
|
values[i] = mat_data.shader_values[(constant.value_offset as usize / 4) + i];
|
|
|
|
}
|
|
|
|
|
2024-04-27 12:58:36 -04:00
|
|
|
constants.push(Constant {
|
|
|
|
id: constant.constant_id,
|
2024-04-27 18:43:49 -04:00
|
|
|
num_values: num_floats as u32,
|
2024-05-18 09:42:07 -04:00
|
|
|
values,
|
2024-04-27 12:58:36 -04:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-01-29 15:07:59 -05:00
|
|
|
Some(Material {
|
|
|
|
shader_package_name,
|
|
|
|
texture_paths,
|
2024-04-20 13:18:03 -04:00
|
|
|
shader_keys: mat_data.shader_keys,
|
2024-04-27 12:58:36 -04:00
|
|
|
constants,
|
2024-04-30 19:51:46 -04:00
|
|
|
samplers: mat_data.samplers,
|
2024-05-04 14:19:55 -04:00
|
|
|
color_table: mat_data.color_table,
|
2024-05-18 09:42:07 -04:00
|
|
|
color_dye_table: mat_data.color_dye_table,
|
2024-01-29 15:07:59 -05:00
|
|
|
})
|
2022-08-11 15:40:14 -04:00
|
|
|
}
|
2022-08-16 11:52:07 -04:00
|
|
|
}
|
2024-04-16 22:05:59 -04:00
|
|
|
|
|
|
|
#[cfg(test)]
|
|
|
|
mod tests {
|
|
|
|
use std::fs::read;
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
|
|
|
use super::*;
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
fn test_invalid() {
|
|
|
|
let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
|
|
|
|
d.push("resources/tests");
|
|
|
|
d.push("random");
|
|
|
|
|
|
|
|
// Feeding it invalid data should not panic
|
|
|
|
Material::from_existing(&read(d).unwrap());
|
|
|
|
}
|
|
|
|
}
|