physis/
shpk.rs

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