rpfm_lib/files/fxc/mod.rs
1//---------------------------------------------------------------------------//
2// Copyright (c) 2017-2026 Ismael Gutiérrez González. All rights reserved.
3//
4// This file is part of the Rusted PackFile Manager (RPFM) project,
5// which can be found here: https://github.com/Frodo45127/rpfm.
6//
7// This file is licensed under the MIT license, which can be found here:
8// https://github.com/Frodo45127/rpfm/blob/master/LICENSE.
9//---------------------------------------------------------------------------//
10
11//! Compiled shader containers (`.fxc`) of the Warscape games.
12//!
13//! These files, under `fxc/` in the packs, hold one compiled DirectX shader each, with the defines and
14//! includes it was compiled with. Newer games use [`HlslCompiled`](crate::files::hlsl_compiled::HlslCompiled) instead.
15//!
16//! # File Format
17//!
18//! All values are little endian:
19//!
20//! ```text
21//! [u32] signature (0x075BCD15)
22//! [u32] version
23//! [u32] define count
24//! [...] defines: name and value, each a u32 size and that many bytes, NUL included
25//! [u32] timestamp of the source
26//! [u32] include count
27//! [...] includes: path (u16 size and that many bytes, no NUL), u32 hash
28//! [...] DXBC shader blob, up to the end of the file
29//! ```
30//!
31//! Only version 4 is known, used by Attila's DX11 shaders.
32//!
33//! # Usage
34//!
35//! ```ignore
36//! use rpfm_lib::files::fxc::Fxc;
37//! use rpfm_lib::files::Decodeable;
38//!
39//! let fxc = Fxc::decode(&mut data, &None)?;
40//! let dxbc = fxc.shader();
41//! ```
42
43use getset::*;
44use serde_derive::{Serialize, Deserialize};
45
46use crate::binary::{ReadBytes, WriteBytes};
47use crate::error::{Result, RLibError};
48use crate::files::{DecodeableExtraData, Decodeable, EncodeableExtraData, Encodeable};
49
50/// File extension of compiled shader containers.
51pub const EXTENSION: &str = ".fxc";
52
53/// Signature at the start of every fxc file.
54pub const SIGNATURE: u32 = 0x075B_CD15;
55
56mod v4;
57
58#[cfg(test)] mod fxc_test;
59
60//---------------------------------------------------------------------------//
61// Enum & Structs
62//---------------------------------------------------------------------------//
63
64/// A compiled shader with the defines and includes it was compiled with.
65#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
66#[getset(get = "pub", get_mut = "pub", set = "pub")]
67pub struct Fxc {
68
69 /// Version of the container format.
70 version: u32,
71
72 /// Preprocessor defines the shader was compiled with, in file order.
73 defines: Vec<FxcDefine>,
74
75 /// Timestamp of the shader's source file.
76 source_timestamp: u32,
77
78 /// Files the shader's source includes, in file order.
79 includes: Vec<FxcInclude>,
80
81 /// The compiled shader: a DXBC blob, with its reflection data.
82 shader: Vec<u8>,
83}
84
85/// A preprocessor define of a compiled shader.
86#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
87#[getset(get = "pub", get_mut = "pub", set = "pub")]
88pub struct FxcDefine {
89
90 /// Name of the define.
91 name: String,
92
93 /// Value of the define.
94 value: String,
95}
96
97/// A file included by the source of a compiled shader.
98#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
99#[getset(get = "pub", get_mut = "pub", set = "pub")]
100pub struct FxcInclude {
101
102 /// Path of the included file.
103 path: String,
104
105 /// Hash of the included file.
106 hash: u32,
107}
108
109//---------------------------------------------------------------------------//
110// Implementations
111//---------------------------------------------------------------------------//
112
113impl Decodeable for Fxc {
114
115 fn decode<R: ReadBytes>(data: &mut R, extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
116 let signature = data.read_u32()?;
117 if signature != SIGNATURE {
118 return Err(RLibError::DecodingFxcUnsupportedSignature(signature));
119 }
120
121 let mut fxc = Self {
122 version: data.read_u32()?,
123 ..Default::default()
124 };
125
126 match fxc.version {
127 4 => fxc.read_v4(data, extra_data)?,
128 _ => return Err(RLibError::DecodingFxcUnsupportedVersion(fxc.version)),
129 }
130
131 Ok(fxc)
132 }
133}
134
135impl Encodeable for Fxc {
136
137 fn encode<W: WriteBytes>(&mut self, buffer: &mut W, extra_data: &Option<EncodeableExtraData>) -> Result<()> {
138 buffer.write_u32(SIGNATURE)?;
139 buffer.write_u32(self.version)?;
140
141 match self.version {
142 4 => self.write_v4(buffer, extra_data)?,
143 _ => return Err(RLibError::EncodingFxcUnsupportedVersion(self.version)),
144 }
145
146 Ok(())
147 }
148}