physis/
shpk.rs

1// SPDX-FileCopyrightText: 2023 Joshua Goins <josh@redstrate.com>
2// SPDX-License-Identifier: GPL-3.0-or-later
3
4use std::io::{Cursor, SeekFrom};
5
6use crate::ByteSpan;
7use crate::crc::XivCrc32;
8use binrw::{BinRead, binread};
9
10#[binread]
11#[br(little, import {
12    strings_offset: u32
13})]
14#[derive(Debug)]
15#[allow(unused)]
16pub struct ResourceParameter {
17    id: u32,
18    #[br(temp)]
19    local_string_offset: u32,
20    #[br(temp)]
21    string_length: u16,
22
23    unknown: u16,
24
25    pub slot: u16,
26    size: u16,
27
28    #[br(seek_before = SeekFrom::Start(strings_offset as u64 + local_string_offset as u64))]
29    #[br(count = string_length, map = | x: Vec<u8> | String::from_utf8(x).unwrap().trim_matches(char::from(0)).to_string())]
30    #[br(restore_position)]
31    pub name: String,
32}
33
34#[binread]
35#[br(little, import {
36    shader_data_offset: u32,
37    strings_offset: u32,
38    is_vertex: bool
39})]
40#[derive(Debug)]
41#[allow(unused)]
42pub struct Shader {
43    data_offset: u32,
44    data_size: u32,
45
46    scalar_parameter_count: u16,
47    resource_parameter_count: u16,
48    uav_parameter_count: u16,
49    texture_count: u16,
50
51    #[br(args { count: scalar_parameter_count as usize, inner: ResourceParameterBinReadArgs { strings_offset }})]
52    pub scalar_parameters: Vec<ResourceParameter>,
53    #[br(args { count: resource_parameter_count as usize, inner: ResourceParameterBinReadArgs { strings_offset }})]
54    pub resource_parameters: Vec<ResourceParameter>,
55    #[br(args { count: uav_parameter_count as usize, inner: ResourceParameterBinReadArgs { strings_offset }})]
56    pub uav_parameters: Vec<ResourceParameter>,
57    #[br(args { count: texture_count as usize, inner: ResourceParameterBinReadArgs { strings_offset }})]
58    pub texture_parameters: Vec<ResourceParameter>,
59
60    /// Additional data specific to the shader type
61    #[br(seek_before = SeekFrom::Start(shader_data_offset as u64 + data_offset as u64))]
62    #[br(count = if is_vertex { shader_data_offset } else { 0 } )]
63    #[br(restore_position)]
64    pub additional_data: Vec<u8>,
65
66    /// The HLSL bytecode of this shader. The DX level used varies.
67    #[br(seek_before = SeekFrom::Start(shader_data_offset as u64 + data_offset as u64 + if is_vertex { 8 } else { 0 } ))]
68    #[br(count = data_size)]
69    #[br(restore_position)]
70    pub bytecode: Vec<u8>,
71}
72
73#[binread]
74#[derive(Debug, Clone, Copy)]
75#[repr(C)]
76#[allow(unused)]
77pub struct MaterialParameter {
78    id: u32,
79    byte_offset: u16,
80    byte_size: u16,
81}
82
83#[binread]
84#[derive(Debug, Clone, Copy)]
85#[repr(C)]
86#[allow(unused)]
87pub struct Key {
88    pub id: u32,
89    pub default_value: u32,
90}
91
92#[binread]
93#[repr(C)]
94#[derive(Debug, Clone, Copy)]
95#[allow(unused)]
96pub struct Pass {
97    id: u32,
98    vertex_shader: u32,
99    pixel_shader: u32,
100}
101
102#[binread]
103#[derive(Debug)]
104#[allow(unused)]
105pub struct NodeAlias {
106    selector: u32,
107    node: u32,
108}
109
110#[binread]
111#[br(little, import {
112    system_key_count: u32,
113    scene_key_count: u32,
114    material_key_count: u32,
115    subview_key_count: u32
116})]
117#[derive(Debug)]
118#[allow(unused)]
119pub struct Node {
120    pub selector: u32,
121    pub pass_count: u32,
122    pub pass_indices: [u8; 16],
123    #[br(count = system_key_count)]
124    pub system_keys: Vec<u32>,
125    #[br(count = scene_key_count)]
126    pub scene_keys: Vec<u32>,
127    #[br(count = material_key_count)]
128    pub material_keys: Vec<u32>,
129    #[br(count = subview_key_count)]
130    pub subview_keys: Vec<u32>,
131    #[br(count = pass_count)]
132    pub passes: Vec<Pass>,
133}
134
135#[binread]
136#[br(little)]
137#[br(magic = b"ShPk")]
138#[derive(Debug)]
139#[allow(dead_code, unused_variables)]
140pub struct ShaderPackage {
141    version: u32,
142
143    // "DX9\0" or "DX11"
144    #[br(count = 4)]
145    #[bw(pad_size_to = 4)]
146    #[bw(map = |x : &String | x.as_bytes())]
147    #[br(map = | x: Vec<u8> | String::from_utf8(x).unwrap().trim_matches(char::from(0)).to_string())]
148    format: String,
149
150    file_length: u32,
151    shader_data_offset: u32,
152    strings_offset: u32,
153
154    vertex_shader_count: u32,
155    pixel_shader_count: u32,
156
157    pub material_parameters_size: u32,
158    material_parameter_count: u16,
159
160    has_mat_param_defaults: u16,
161    scalar_parameter_count: u16,
162    #[br(temp)]
163    unknown1: u16,
164    sampler_count: u16,
165    texture_count: u16,
166    uav_count: u16,
167    #[br(temp)]
168    unknown2: u16,
169
170    system_key_count: u32,
171    scene_key_count: u32,
172    material_key_count: u32,
173    node_count: u32,
174    node_alias_count: u32,
175
176    // TODO: dx9 needs 4 bytes of padding, dx11 is 8 (correct)
177    #[br(args { count: vertex_shader_count as usize, inner : ShaderBinReadArgs { is_vertex: true, shader_data_offset, strings_offset }})]
178    pub vertex_shaders: Vec<Shader>,
179    #[br(args { count: pixel_shader_count as usize, inner: ShaderBinReadArgs { is_vertex: false, shader_data_offset, strings_offset } })]
180    pub pixel_shaders: Vec<Shader>,
181
182    #[br(count = material_parameter_count)]
183    pub material_parameters: Vec<MaterialParameter>,
184
185    #[br(count = if has_mat_param_defaults == 0x1 { (material_parameters_size as i32) >> 2i32 } else { 0 })]
186    mat_param_defaults: Vec<f32>,
187
188    #[br(args { count: scalar_parameter_count as usize, inner: ResourceParameterBinReadArgs { strings_offset }})]
189    scalar_parameters: Vec<ResourceParameter>,
190    #[br(args { count: sampler_count as usize, inner: ResourceParameterBinReadArgs { strings_offset }})]
191    sampler_parameters: Vec<ResourceParameter>,
192    #[br(args { count: texture_count as usize, inner: ResourceParameterBinReadArgs { strings_offset }})]
193    texture_parameters: Vec<ResourceParameter>,
194    #[br(args { count: uav_count as usize, inner: ResourceParameterBinReadArgs { strings_offset }})]
195    uav_parameters: Vec<ResourceParameter>,
196
197    #[br(count = system_key_count)]
198    pub system_keys: Vec<Key>,
199    #[br(count = scene_key_count)]
200    pub scene_keys: Vec<Key>,
201    #[br(count = material_key_count)]
202    pub material_keys: Vec<Key>,
203
204    pub sub_view_key1_default: u32,
205    pub sub_view_key2_default: u32,
206
207    #[br(args { count: node_count as usize, inner: NodeBinReadArgs { system_key_count, scene_key_count, material_key_count, subview_key_count: 2 }})]
208    pub nodes: Vec<Node>,
209
210    #[br(ignore)]
211    node_selectors: Vec<(u32, u32)>,
212
213    #[br(count = node_alias_count)]
214    node_aliases: Vec<NodeAlias>,
215}
216
217const SELECTOR_MULTIPLER: u32 = 31;
218
219impl ShaderPackage {
220    /// Reads an existing SHPK file
221    pub fn from_existing(buffer: ByteSpan) -> Option<ShaderPackage> {
222        let mut cursor = Cursor::new(buffer);
223        let mut package = ShaderPackage::read(&mut cursor).ok()?;
224
225        for (i, node) in package.nodes.iter().enumerate() {
226            package.node_selectors.push((node.selector, i as u32));
227        }
228        for alias in &package.node_aliases {
229            package.node_selectors.push((alias.selector, alias.node));
230        }
231
232        Some(package)
233    }
234
235    pub fn find_node(&self, selector: u32) -> Option<&Node> {
236        for (sel, node) in &self.node_selectors {
237            if *sel == selector {
238                return Some(&self.nodes[*node as usize]);
239            }
240        }
241
242        None
243    }
244
245    pub fn build_selector_from_all_keys(
246        system_keys: &[u32],
247        scene_keys: &[u32],
248        material_keys: &[u32],
249        subview_keys: &[u32],
250    ) -> u32 {
251        Self::build_selector_from_keys(
252            Self::build_selector(system_keys),
253            Self::build_selector(scene_keys),
254            Self::build_selector(material_keys),
255            Self::build_selector(subview_keys),
256        )
257    }
258
259    pub fn build_selector_from_keys(
260        system_key: u32,
261        scene_key: u32,
262        material_key: u32,
263        subview_key: u32,
264    ) -> u32 {
265        Self::build_selector(&[system_key, scene_key, material_key, subview_key])
266    }
267
268    pub fn build_selector(keys: &[u32]) -> u32 {
269        let mut selector: u32 = 0;
270        let mut multiplier: u32 = 1;
271
272        for key in keys {
273            selector = selector.wrapping_add(key.wrapping_mul(multiplier));
274            multiplier = multiplier.wrapping_mul(SELECTOR_MULTIPLER);
275        }
276
277        selector
278    }
279
280    pub fn crc(str: &str) -> u32 {
281        XivCrc32::from(str).crc
282    }
283}
284
285#[cfg(test)]
286mod tests {
287    use std::fs::read;
288    use std::path::PathBuf;
289
290    use super::*;
291
292    #[test]
293    fn test_invalid() {
294        let mut d = PathBuf::from(env!("CARGO_MANIFEST_DIR"));
295        d.push("resources/tests");
296        d.push("random");
297
298        // Feeding it invalid data should not panic
299        ShaderPackage::from_existing(&read(d).unwrap());
300    }
301
302    #[test]
303    fn test_crc() {
304        assert_eq!(ShaderPackage::crc("PASS_0"), 0xC5A5389C);
305        assert_eq!(ShaderPackage::crc("DecodeDepthBuffer"), 0x2C6C023C);
306    }
307
308    #[test]
309    fn test_selector() {
310        let selector = ShaderPackage::build_selector_from_all_keys(
311            &[],
312            &[
313                ShaderPackage::crc("TransformViewSkin"),
314                ShaderPackage::crc("GetAmbientLight_SH"),
315                ShaderPackage::crc("GetReflectColor_Texture"),
316                ShaderPackage::crc("GetAmbientOcclusion_None"),
317                ShaderPackage::crc("ApplyDitherClipOff"),
318            ],
319            &[3756477356, 1556481461, 1111668802, 428675533],
320            &[
321                ShaderPackage::crc("Default"),
322                ShaderPackage::crc("SUB_VIEW_MAIN"),
323            ],
324        );
325
326        assert_eq!(selector, 0x1075AE91);
327    }
328}