rpfm_lib/files/dat/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//! DAT audio configuration file format support.
12//!
13//! DAT files (`.dat`) are audio configuration files used in Total War games to define
14//! sound events, parameters, and enumerations. These files work in conjunction with
15//! Wwise sound banks to configure audio playback behavior.
16//!
17//! # File Format
18//!
19//! DAT files are binary files containing six data blocks:
20//! - **Event 0**: Event parameters (name-value pairs with float values)
21//! - **Block 1**: Event enumerations (name with list of enumeration values)
22//! - **Block 2**: Event enumerations (name with list of enumeration values)
23//! - **Voice Events**: Voice event enumerations (name with list of voice event values)
24//! - **Block 4**: Event enumeration list (simple string list)
25//! - **Block 5**: Event enumeration list (simple string list)
26//!
27//! # Purpose
28//!
29//! DAT files define audio event configurations including:
30//! - Sound event parameters and their default values
31//! - Available enumeration values for event properties
32//! - Groupings and categorizations of audio events
33//!
34//! # Usage
35//!
36//! ```rust,ignore
37//! use rpfm_lib::files::dat::Dat;
38//! use rpfm_lib::files::Decodeable;
39//!
40//! // Decode from binary data
41//! let dat = Dat::decode(&mut data, &None)?;
42//!
43//! // Access event parameters
44//! for (event_name, value) in dat.event_0() {
45//! println!("Parameter '{}' = {}", event_name, value);
46//! }
47//!
48//! // Access voice events
49//! for (voice_event, values) in dat.voice_events() {
50//! println!("Voice event '{}' has {} values", voice_event, values.len());
51//! }
52//! ```
53//!
54//! # File Location
55//!
56//! These files are typically found at:
57//! - `sound/*.dat`
58//! - `audio/wwisedata/*.dat`
59
60use getset::*;
61use serde_derive::{Serialize, Deserialize};
62
63use crate::binary::{ReadBytes, WriteBytes};
64use crate::error::Result;
65use crate::files::{Decodeable, EncodeableExtraData, Encodeable};
66use crate::utils::*;
67
68use super::DecodeableExtraData;
69
70/// File extension for DAT audio configuration files.
71pub const EXTENSION: &str = ".dat";
72
73//#[cfg(test)] mod test_dat;
74
75//---------------------------------------------------------------------------//
76// Enum & Structs
77//---------------------------------------------------------------------------//
78
79/// Represents a DAT audio configuration file decoded in memory.
80///
81/// Contains six blocks of audio event configuration data including parameters,
82/// enumerations, and event lists used to configure Wwise sound playback.
83///
84/// # Structure
85///
86/// The file is organized into six sequential blocks, each serving a different purpose
87/// in the audio configuration system. The exact semantic meaning of each block may
88/// vary between Total War games.
89///
90/// # Fields
91///
92/// * `event_0` - Event parameters with float values (e.g., volume, pitch defaults)
93/// * `block_1` - Event enumerations with multiple values (e.g., sound categories)
94/// * `block_2` - Event enumerations with multiple values (e.g., sound categories)
95/// * `voice_events` - Voice event enumerations with associated values
96/// * `block_4` - Simple event enumeration list
97/// * `block_5` - Simple event enumeration list
98///
99/// # Examples
100///
101/// ```rust,ignore
102/// let dat = Dat::decode(&mut data, &None)?;
103///
104/// // Iterate through event parameters
105/// for (name, value) in dat.event_0() {
106/// println!("Parameter: {} = {}", name, value);
107/// }
108///
109/// // Iterate through voice events
110/// for (voice_event, voice_values) in dat.voice_events() {
111/// for value in voice_values {
112/// println!(" {} -> {}", voice_event, value);
113/// }
114/// }
115/// ```
116#[derive(Default, PartialEq, Clone, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
117#[getset(get = "pub", get_mut = "pub", set = "pub")]
118pub struct Dat {
119 /// Event parameters with floating-point values.
120 ///
121 /// Maps event parameter names to their default float values.
122 /// Typically used for parameters like volume, pitch, or other numeric settings.
123 event_0: Vec<(String, f32)>,
124
125 /// Event enumerations with string lists.
126 ///
127 /// Maps enumeration names to lists of possible string values.
128 /// Used for categorical audio properties.
129 block_1: Vec<(String, Vec<String>)>,
130
131 /// Event enumerations with string lists.
132 ///
133 /// Maps enumeration names to lists of possible string values.
134 /// Similar to block_1, potentially for different enumeration categories.
135 block_2: Vec<(String, Vec<String>)>,
136
137 /// Voice event enumerations with string lists.
138 ///
139 /// Maps voice event names to lists of possible voice event values.
140 /// Used for categorizing and organizing voice-related audio events.
141 voice_events: Vec<(String, Vec<String>)>,
142
143 /// Simple event enumeration list.
144 ///
145 /// List of event enumeration names without associated values.
146 /// FIXME: Possible wrong implementation.
147 block_4: Vec<String>,
148
149 /// Simple event enumeration list.
150 ///
151 /// List of event enumeration names without associated values.
152 /// Similar to block_4, potentially for a different category.
153 block_5: Vec<String>,
154}
155
156//---------------------------------------------------------------------------//
157// Implementation of Dat
158//---------------------------------------------------------------------------//
159
160impl Decodeable for Dat {
161
162 fn decode<R: ReadBytes>(data: &mut R, _extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
163 let mut decoded = Self::default();
164 let data_len = data.len()?;
165
166 for _ in 0..data.read_u32()? {
167 let string_size = data.read_u32()?;
168 let event_name = data.read_string_u8(string_size as usize)?;
169 let event_value = data.read_f32()?;
170 decoded.event_0.push((event_name, event_value));
171 }
172
173 for _ in 0..data.read_u32()? {
174 let string_size = data.read_u32()?;
175 let event_enum = data.read_string_u8(string_size as usize)?;
176
177 let mut entries = vec![];
178 for _ in 0..data.read_u32()? {
179 let string_size = data.read_u32()?;
180 let event_enum = data.read_string_u8(string_size as usize)?;
181 entries.push(event_enum);
182 }
183 decoded.block_1.push((event_enum, entries));
184 }
185
186 for _ in 0..data.read_u32()? {
187 let string_size = data.read_u32()?;
188 let event_enum = data.read_string_u8(string_size as usize)?;
189
190 let mut entries = vec![];
191 for _ in 0..data.read_u32()? {
192 let string_size = data.read_u32()?;
193 let event_enum = data.read_string_u8(string_size as usize)?;
194 entries.push(event_enum);
195 }
196 decoded.block_2.push((event_enum, entries));
197 }
198
199 for _ in 0..data.read_u32()? {
200 let string_size = data.read_u32()?;
201 let event_enum = data.read_string_u8(string_size as usize)?;
202
203 let mut entries = vec![];
204 for _ in 0..data.read_u32()? {
205 let string_size = data.read_u32()?;
206 let event_enum = data.read_string_u8(string_size as usize)?;
207 entries.push(event_enum);
208 }
209 decoded.voice_events.push((event_enum, entries));
210 }
211
212 for _ in 0..data.read_u32()? {
213 let string_size = data.read_u32()?;
214 let event_enum = data.read_string_u8(string_size as usize)?;
215 decoded.block_4.push(event_enum);
216 }
217
218 for _ in 0..data.read_u32()? {
219 let string_size = data.read_u32()?;
220 let event_enum = data.read_string_u8(string_size as usize)?;
221 decoded.block_5.push(event_enum);
222 }
223
224 check_size_mismatch(data.stream_position()? as usize, data_len as usize)?;
225 Ok(decoded)
226 }
227}
228
229impl Encodeable for Dat {
230
231 fn encode<W: WriteBytes>(&mut self, buffer: &mut W, _extra_data: &Option<EncodeableExtraData>) -> Result<()> {
232 buffer.write_u32(self.event_0.len() as u32)?;
233 for entry in self.event_0() {
234 buffer.write_u32(entry.0.len() as u32)?;
235 buffer.write_string_u8(&entry.0)?;
236 buffer.write_f32(entry.1)?;
237 }
238
239 buffer.write_u32(self.block_1.len() as u32)?;
240 for entry in self.block_1() {
241 buffer.write_u32(entry.0.len() as u32)?;
242 buffer.write_string_u8(&entry.0)?;
243
244 buffer.write_u32(entry.1.len() as u32)?;
245 for subentry in &entry.1 {
246 buffer.write_u32(subentry.len() as u32)?;
247 buffer.write_string_u8(subentry)?;
248 }
249 }
250
251 buffer.write_u32(self.block_2.len() as u32)?;
252 for entry in self.block_2() {
253 buffer.write_u32(entry.0.len() as u32)?;
254 buffer.write_string_u8(&entry.0)?;
255
256 buffer.write_u32(entry.1.len() as u32)?;
257 for subentry in &entry.1 {
258 buffer.write_u32(subentry.len() as u32)?;
259 buffer.write_string_u8(subentry)?;
260 }
261 }
262
263 buffer.write_u32(self.voice_events.len() as u32)?;
264 for entry in self.voice_events() {
265 buffer.write_u32(entry.0.len() as u32)?;
266 buffer.write_string_u8(&entry.0)?;
267
268 buffer.write_u32(entry.1.len() as u32)?;
269 for subentry in &entry.1 {
270 buffer.write_u32(subentry.len() as u32)?;
271 buffer.write_string_u8(subentry)?;
272 }
273 }
274
275 buffer.write_u32(self.block_4.len() as u32)?;
276 for entry in self.block_4() {
277 buffer.write_u32(entry.len() as u32)?;
278 buffer.write_string_u8(entry)?;
279 }
280
281 buffer.write_u32(self.block_5.len() as u32)?;
282 for entry in self.block_5() {
283 buffer.write_u32(entry.len() as u32)?;
284 buffer.write_string_u8(entry)?;
285 }
286
287 Ok(())
288 }
289}