Skip to main content

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, or 0xF8C5DEAD in Rome 2)
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 Rome 2's and Attila's DX11 shaders. The two games differ only in the signature.
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 the fxc files of Attila and later games.
54pub const SIGNATURE: u32 = 0x075B_CD15;
55
56/// Signature at the start of the fxc files of Rome 2.
57pub const SIGNATURE_ROME_2: u32 = 0xF8C5_DEAD;
58
59mod v4;
60
61#[cfg(test)] mod fxc_test;
62
63//---------------------------------------------------------------------------//
64//                              Enum & Structs
65//---------------------------------------------------------------------------//
66
67/// A compiled shader with the defines and includes it was compiled with.
68#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
69#[getset(get = "pub", get_mut = "pub", set = "pub")]
70pub struct Fxc {
71
72    /// Signature of the file: [`SIGNATURE`] or [`SIGNATURE_ROME_2`]. `0` writes [`SIGNATURE`].
73    signature: u32,
74
75    /// Version of the container format.
76    version: u32,
77
78    /// Preprocessor defines the shader was compiled with, in file order.
79    defines: Vec<FxcDefine>,
80
81    /// Timestamp of the shader's source file.
82    source_timestamp: u32,
83
84    /// Files the shader's source includes, in file order.
85    includes: Vec<FxcInclude>,
86
87    /// The compiled shader: a DXBC blob, with its reflection data.
88    shader: Vec<u8>,
89}
90
91/// A preprocessor define of a compiled shader.
92#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
93#[getset(get = "pub", get_mut = "pub", set = "pub")]
94pub struct FxcDefine {
95
96    /// Name of the define.
97    name: String,
98
99    /// Value of the define.
100    value: String,
101}
102
103/// A file included by the source of a compiled shader.
104#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
105#[getset(get = "pub", get_mut = "pub", set = "pub")]
106pub struct FxcInclude {
107
108    /// Path of the included file.
109    path: String,
110
111    /// Hash of the included file.
112    hash: u32,
113}
114
115//---------------------------------------------------------------------------//
116//                              Implementations
117//---------------------------------------------------------------------------//
118
119impl Decodeable for Fxc {
120
121    fn decode<R: ReadBytes>(data: &mut R, extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
122        let signature = data.read_u32()?;
123        if signature != SIGNATURE && signature != SIGNATURE_ROME_2 {
124            return Err(RLibError::DecodingFxcUnsupportedSignature(signature));
125        }
126
127        let mut fxc = Self {
128            signature,
129            version: data.read_u32()?,
130            ..Default::default()
131        };
132
133        match fxc.version {
134            4 => fxc.read_v4(data, extra_data)?,
135            _ => return Err(RLibError::DecodingFxcUnsupportedVersion(fxc.version)),
136        }
137
138        Ok(fxc)
139    }
140}
141
142impl Encodeable for Fxc {
143
144    fn encode<W: WriteBytes>(&mut self, buffer: &mut W, extra_data: &Option<EncodeableExtraData>) -> Result<()> {
145        buffer.write_u32(if self.signature == 0 { SIGNATURE } else { self.signature })?;
146        buffer.write_u32(self.version)?;
147
148        match self.version {
149            4 => self.write_v4(buffer, extra_data)?,
150            _ => return Err(RLibError::EncodingFxcUnsupportedVersion(self.version)),
151        }
152
153        Ok(())
154    }
155}