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}