Skip to main content

rpfm_lib/files/esf/
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//! This module provides support for reading and writing ESF (Empire Save File) files.
12//!
13//! # Overview
14//!
15//! ESF files are hierarchical binary files used extensively in Total War games to store
16//! structured game data. They are found in various contexts including:
17//!
18//! - Campaign save games (`.save`, `.save_multiplayer`)
19//! - Campaign startup positions (startpos)
20//! - Character save files (`.twc`)
21//! - Composite Scene files (`.csc`)
22//! - Campaign Effect Object files (`.ccd`)
23//! - General ESF data (`.esf`)
24//!
25//! # File Structure
26//!
27//! An ESF file consists of:
28//!
29//! 1. **Header**: Contains the signature, unknown field, creation date, and offset to string tables.
30//! 2. **Node Tree**: A recursive tree structure starting from a root record node, containing
31//!    all the actual data organized hierarchically.
32//! 3. **String Tables**: Three separate tables at the end of the file:
33//!    - Record names: Names used by record nodes (e.g., "CAMPAIGN_SAVE_GAME", "FACTION")
34//!    - UTF-16 strings: String values referenced by UTF-16 string nodes
35//!    - UTF-8/ASCII strings: String values referenced by ASCII string nodes
36//!
37//! # Supported Signatures
38//!
39//! ESF files come in several format versions identified by their signature:
40//!
41//! | Signature | Status        | Notes                                   |
42//! |-----------|---------------|-----------------------------------------|
43//! | CAAB      | ✅ Supported  | Older format, uses u16 for string sizes |
44//! | CBAB      | ✅ Supported  | Newer format, uses u32 for string sizes |
45//! | CEAB      | ❌ Unsupported | Rare format                             |
46//! | CFAB      | ❌ Unsupported | Rare format                             |
47//!
48//! # Node Types
49//!
50//! The ESF format supports a rich set of node types organized into categories:
51//!
52//! ## Primitive Types
53//! - Boolean, signed/unsigned integers (8/16/32/64-bit), floats (32/64-bit)
54//! - 2D and 3D coordinates (pairs/triplets of f32)
55//! - UTF-16 and ASCII strings (stored as indices into string tables)
56//! - Angles (i16)
57//!
58//! ## Optimized Primitives
59//! Many primitive types have optimized encodings for common values to reduce file size:
60//! - `BOOL_TRUE`/`BOOL_FALSE`: Single byte instead of marker + value
61//! - `U32_ZERO`/`U32_ONE`: Single byte for 0 or 1
62//! - `U32_BYTE`/`U32_16BIT`/`U32_24BIT`: Smaller encodings when value fits
63//! - Similar optimizations exist for i32 and f32 (zero)
64//!
65//! ## Arrays
66//! All primitive types have corresponding array variants that store multiple values
67//! with a length prefix. Arrays also support optimized encodings.
68//!
69//! ## Record Nodes
70//! Record nodes are container nodes that hold other nodes. They have:
71//! - A name (from the record names table)
72//! - A version number
73//! - Flags controlling encoding behavior
74//! - Children organized into groups (for nested blocks) or a single list
75//!
76//! # Compression
77//!
78//! Large ESF files (particularly campaign startpos files) may contain compressed sections.
79//! Nodes with specific names (e.g., `CAMPAIGN_ENV`) are automatically compressed using LZMA1
80//! during encoding. The compressed data is stored in special `COMPRESSED_DATA` and
81//! `COMPRESSED_DATA_INFO` record nodes.
82//!
83//! During decoding, these compressed sections are automatically decompressed and the
84//! contained ESF data replaces the outer structure.
85//!
86//! # Usage
87//!
88//! ```ignore
89//! use rpfm_lib::files::esf::ESF;
90//! use rpfm_lib::files::{Decodeable, Encodeable};
91//! use std::io::Cursor;
92//!
93//! // Decode an ESF file
94//! let mut reader = Cursor::new(esf_bytes);
95//! let esf = ESF::decode(&mut reader, &None)?;
96//!
97//! // Access the root node and traverse the tree
98//! let root = esf.root_node();
99//!
100//! // Encode back to bytes
101//! let mut output = Vec::new();
102//! esf.encode(&mut output, &None)?;
103//! ```
104//!
105//! # Internal Submodules
106//!
107//! - `caab`: CAAB format-specific reading and writing logic
108//! - `cbab`: CBAB format-specific reading and writing logic
109//! - `utils`: Shared utilities for node reading/writing across formats
110
111use bitflags::bitflags;
112use getset::*;
113use serde_derive::{Serialize, Deserialize};
114
115use std::{fmt, fmt::Display};
116
117use crate::binary::{ReadBytes, WriteBytes};
118use crate::error::{Result, RLibError};
119use crate::files::{DecodeableExtraData, Decodeable, EncodeableExtraData, Encodeable};
120
121/// File extensions associated with ESF files.
122///
123/// These extensions are used to identify files that should be parsed as ESF format.
124pub const EXTENSIONS: [&str; 6] = [
125    ".csc",                 // Composite Scene files
126    ".ccd",                 // CEO (Campaign Effect Object) files
127    ".esf",                 // Generic ESF data files
128    ".save",                // Single-player save game files
129    ".save_multiplayer",    // Multiplayer save game files
130    ".twc",                 // Total War Character save files
131];
132
133/// Magic bytes identifying the CAAB ESF format (older format with u16 string sizes).
134pub const SIGNATURE_CAAB: &[u8; 4] = &[0xCA, 0xAB, 0x00, 0x00];
135
136/// Magic bytes identifying the CBAB ESF format (newer format with u32 string sizes).
137pub const SIGNATURE_CBAB: &[u8; 4] = &[0xCB, 0xAB, 0x00, 0x00];
138
139/// Magic bytes identifying the CEAB ESF format (unsupported).
140pub const SIGNATURE_CEAB: &[u8; 4] = &[0xCE, 0xAB, 0x00, 0x00];
141
142/// Magic bytes identifying the CFAB ESF format (unsupported).
143pub const SIGNATURE_CFAB: &[u8; 4] = &[0xCF, 0xAB, 0x00, 0x00];
144
145mod caab;
146mod cbab;
147mod stream;
148mod utils;
149
150pub use self::utils::hash_map_order;
151
152#[cfg(test)] mod esf_test;
153
154//---------------------------------------------------------------------------//
155//                              Node Type Markers
156//---------------------------------------------------------------------------//
157//
158// These byte markers identify the type of each node in the ESF binary format.
159// The marker system was originally documented in ESFEdit.
160//
161// Markers are organized into ranges:
162// - 0x00:        Invalid/reserved
163// - 0x01-0x10:   Primitive types (bool, integers, floats, strings, etc.)
164// - 0x12-0x1d:   Optimized primitives (compact encodings for common values)
165// - 0x21-0x26:   Unknown/undocumented types
166// - 0x41-0x50:   Arrays of primitive types
167// - 0x52-0x5d:   Arrays with optimized element encodings
168// - 0x80+:       Record nodes (high bit set indicates record type)
169
170/// Invalid marker - encountering this during parsing is always an error.
171pub const INVALID: u8 = 0x00;
172
173// Primitive type markers (0x01-0x10)
174const BOOL: u8 = 0x01;      // Boolean value (1 byte: 0 or 1)
175const I8: u8 = 0x02;        // Signed 8-bit integer
176const I16: u8 = 0x03;       // Signed 16-bit integer (little-endian)
177const I32: u8 = 0x04;       // Signed 32-bit integer (little-endian)
178const I64: u8 = 0x05;       // Signed 64-bit integer (little-endian)
179const U8: u8 = 0x06;        // Unsigned 8-bit integer
180const U16: u8 = 0x07;       // Unsigned 16-bit integer (little-endian)
181const U32: u8 = 0x08;       // Unsigned 32-bit integer (little-endian)
182const U64: u8 = 0x09;       // Unsigned 64-bit integer (little-endian)
183const F32: u8 = 0x0a;       // 32-bit floating point (IEEE 754)
184const F64: u8 = 0x0b;       // 64-bit floating point (IEEE 754)
185const COORD_2D: u8 = 0x0c;  // 2D coordinate (two f32 values: x, y)
186const COORD_3D: u8 = 0x0d;  // 3D coordinate (three f32 values: x, y, z)
187const UTF16: u8 = 0x0e;     // UTF-16 string (stored as index into string table)
188const ASCII: u8 = 0x0f;     // ASCII/UTF-8 string (stored as index into string table)
189const ANGLE: u8 = 0x10;     // Angle value (i16, likely representing degrees or radians scaled)
190
191// Optimized primitive markers (0x12-0x1d)
192// These provide compact encodings for common values to reduce file size.
193const BOOL_TRUE: u8 = 0x12;     // Boolean true (no additional bytes needed)
194const BOOL_FALSE: u8 = 0x13;    // Boolean false (no additional bytes needed)
195const U32_ZERO: u8 = 0x14;      // u32 value of 0 (no additional bytes)
196const U32_ONE: u8 = 0x15;       // u32 value of 1 (no additional bytes)
197const U32_BYTE: u8 = 0x16;      // u32 stored as single byte (0-255)
198const U32_16BIT: u8 = 0x17;     // u32 stored as 2 bytes (0-65535)
199const U32_24BIT: u8 = 0x18;     // u32 stored as 3 bytes (0-16777215)
200const I32_ZERO: u8 = 0x19;      // i32 value of 0 (no additional bytes)
201const I32_BYTE: u8 = 0x1a;      // i32 stored as single signed byte (-128 to 127)
202const I32_16BIT: u8 = 0x1b;     // i32 stored as 2 bytes (-32768 to 32767)
203const I32_24BIT: u8 = 0x1c;     // i32 stored as 3 bytes (-8388608 to 8388607)
204const F32_ZERO: u8 = 0x1d;      // f32 value of 0.0 (no additional bytes)
205
206// Unknown/undocumented type markers (0x21-0x26)
207// These types exist in some ESF files but their exact purpose is unclear.
208const UNKNOWN_21: u8 = 0x21;    // Unknown type, stores u32
209const UNKNOWN_23: u8 = 0x23;    // Unknown type, stores u8
210const UNKNOWN_24: u8 = 0x24;    // Unknown type, stores u16
211const UNKNOWN_25: u8 = 0x25;    // Unknown type, stores u32
212
213// Three Kingdoms DLC "Eight Princes" introduced this type
214const UNKNOWN_26: u8 = 0x26;    // Variable-length unknown type with special encoding
215
216// Array type markers (0x41-0x50)
217// Arrays store multiple values of the same type with a CAULEB128-encoded length prefix.
218const BOOL_ARRAY: u8 = 0x41;
219const I8_ARRAY: u8 = 0x42;
220const I16_ARRAY: u8 = 0x43;
221const I32_ARRAY: u8 = 0x44;
222const I64_ARRAY: u8 = 0x45;
223const U8_ARRAY: u8 = 0x46;
224const U16_ARRAY: u8 = 0x47;
225const U32_ARRAY: u8 = 0x48;
226const U64_ARRAY: u8 = 0x49;
227const F32_ARRAY: u8 = 0x4a;
228const F64_ARRAY: u8 = 0x4b;
229const COORD_2D_ARRAY: u8 = 0x4c;
230const COORD_3D_ARRAY: u8 = 0x4d;
231const UTF16_ARRAY: u8 = 0x4e;
232const ASCII_ARRAY: u8 = 0x4f;
233const ANGLE_ARRAY: u8 = 0x50;
234
235// Optimized array markers (0x52-0x5d)
236// Arrays where elements use compact encodings. Some markers exist in the format
237// but don't make practical sense (e.g., array of all zeros/ones).
238const BOOL_TRUE_ARRAY: u8 = 0x52;   // Unused - array of all true makes no sense
239const BOOL_FALSE_ARRAY: u8 = 0x53;  // Unused - array of all false makes no sense
240const U32_ZERO_ARRAY: u8 = 0x54;    // Unused - array of all zeros makes no sense
241const U32_ONE_ARRAY: u8 = 0x55;     // Unused - array of all ones makes no sense
242const U32_BYTE_ARRAY: u8 = 0x56;    // u32 array with each element stored as 1 byte
243const U32_16BIT_ARRAY: u8 = 0x57;   // u32 array with each element stored as 2 bytes
244const U32_24BIT_ARRAY: u8 = 0x58;   // u32 array with each element stored as 3 bytes
245const I32_ZERO_ARRAY: u8 = 0x59;    // Unused - array of all zeros makes no sense
246const I32_BYTE_ARRAY: u8 = 0x5a;    // i32 array with each element stored as 1 byte
247const I32_16BIT_ARRAY: u8 = 0x5b;   // i32 array with each element stored as 2 bytes
248const I32_24BIT_ARRAY: u8 = 0x5c;   // i32 array with each element stored as 3 bytes
249const F32_ZERO_ARRAY: u8 = 0x5d;    // Unused - array of all zeros makes no sense
250
251// Compression-related constants
252// Large nodes (e.g., campaign environment data) may be LZMA1-compressed.
253/// Record names that trigger automatic compression during encoding.
254const COMPRESSED_TAGS: [&str; 1] = ["CAMPAIGN_ENV"];
255/// Name of the record node containing compressed data bytes.
256const COMPRESSED_DATA_TAG: &str = "COMPRESSED_DATA";
257/// Name of the record node containing compression metadata (uncompressed size + LZMA header).
258const COMPRESSED_DATA_INFO_TAG: &str = "COMPRESSED_DATA_INFO";
259
260// Record nodes use the high bit of their type byte to indicate they are records,
261// with additional flag bits controlling encoding behavior.
262bitflags! {
263
264    /// Flags that control how a record node is encoded in the ESF binary format.
265    ///
266    /// These flags are stored in the high bits of the type byte for record nodes.
267    /// The combination of flags determines how the node header and children are encoded.
268    ///
269    /// # Binary Layout
270    ///
271    /// For a record node's first byte:
272    /// - Bit 7 (0x80): Always set for record nodes (`IS_RECORD_NODE`)
273    /// - Bit 6 (0x40): Set if node has nested blocks (`HAS_NESTED_BLOCKS`)
274    /// - Bit 5 (0x20): Set if using non-optimized header format (`HAS_NON_OPTIMIZED_INFO`)
275    /// - Bits 0-4: Used for optimized encoding of version/name when bit 5 is clear
276    #[derive(PartialEq, Clone, Copy, Default, Debug, Serialize, Deserialize)]
277    pub struct RecordNodeFlags: u8 {
278
279        /// Indicates this is a record node (container with children).
280        /// This flag is always set for record nodes and distinguishes them from primitive nodes.
281        const IS_RECORD_NODE            = 0b1000_0000;
282
283        /// Indicates the record contains multiple groups of children (nested blocks).
284        /// When set, children are organized into separate groups, each with its own size prefix.
285        /// When clear, all children are in a single flat list.
286        const HAS_NESTED_BLOCKS         = 0b0100_0000;
287
288        /// Indicates the node uses the full 3-byte header format (u16 name index + u8 version).
289        /// When clear, the header is compressed into 2 bytes using bitwise encoding:
290        /// - Bits 1-4: Version (4 bits, max 15)
291        /// - Bit 0 + next byte: Name index (9 bits, max 511)
292        const HAS_NON_OPTIMIZED_INFO    = 0b0010_0000;
293    }
294}
295
296//---------------------------------------------------------------------------//
297//                              Enum & Structs
298//---------------------------------------------------------------------------//
299
300/// Represents a complete ESF file decoded in memory.
301///
302/// An ESF file contains a tree of nodes starting from a single root node. The root node
303/// is always a record node that contains all other data in the file.
304///
305/// # Fields
306///
307/// - `signature`: Identifies the format version (CAAB, CBAB, etc.)
308/// - `unknown_1`: Purpose unknown, typically 0
309/// - `creation_date`: Unix timestamp or similar date value
310/// - `root_node`: The top-level record node containing all file data
311#[derive(Getters, Setters, PartialEq, Clone, Debug, Default, Serialize, Deserialize)]
312#[getset(get = "pub", set = "pub")]
313pub struct ESF {
314
315    /// Format signature identifying the ESF version (CAAB, CBAB, etc.).
316    signature: ESFSignature,
317
318    /// Unknown header field, typically 0. May be reserved for future use.
319    unknown_1: u32,
320
321    /// Creation timestamp of the ESF file.
322    creation_date: u32,
323
324    /// Root node of the node tree containing all ESF data.
325    /// This is always a record node in valid ESF files.
326    root_node: NodeType,
327
328    /// If the string tables are written in index order (Rome 2's tools) instead of the order of the games' string hash maps.
329    /// Decoding sets it for a file written so.
330    #[serde(default)]
331    strings_in_index_order: bool,
332}
333
334/// Identifies the format version of an ESF file.
335///
336/// Different signatures indicate different encoding rules, particularly for
337/// string size prefixes and potentially other format variations.
338#[derive(Eq, PartialEq, Clone, Copy, Debug, Default, Serialize, Deserialize)]
339pub enum ESFSignature {
340    /// CAAB format - older version using u16 for string size prefixes.
341    #[default]
342    CAAB,
343    /// CBAB format - newer version using u32 for string size prefixes.
344    CBAB,
345    /// CEAB format - unsupported, rarely encountered.
346    CEAB,
347    /// CFAB format - unsupported, rarely encountered.
348    CFAB
349}
350
351/// Represents all possible node types in an ESF file.
352///
353/// ESF files use a tagged union approach where each node in the binary format
354/// starts with a type marker byte that identifies what kind of data follows.
355/// This enum mirrors that structure.
356///
357/// # Categories
358///
359/// - **Primitive nodes**: Single values (bool, integers, floats, strings)
360/// - **Optimized nodes**: Primitives with compact encoding tracking (`BoolNode`, `I32Node`, etc.)
361/// - **Complex nodes**: Structured data like coordinates
362/// - **Array nodes**: Collections of same-typed values
363/// - **Record nodes**: Container nodes with named children
364/// - **Unknown nodes**: Undocumented types preserved for round-trip fidelity
365///
366/// Note: Some type information was originally extracted from ESFEdit.
367#[derive(PartialEq, Clone, Debug, Default, Serialize, Deserialize)]
368pub enum NodeType {
369
370    /// Invalid/uninitialized node. Used as a placeholder; encountering this during
371    /// parsing or encoding indicates an error.
372    #[default]
373    Invalid,
374
375    // Primitive nodes
376    /// Boolean value with optimization tracking.
377    Bool(BoolNode),
378    /// Signed 8-bit integer.
379    I8(i8),
380    /// Signed 16-bit integer.
381    I16(i16),
382    /// Signed 32-bit integer with optimization tracking.
383    I32(I32Node),
384    /// Signed 64-bit integer.
385    I64(i64),
386    /// Unsigned 8-bit integer.
387    U8(u8),
388    /// Unsigned 16-bit integer.
389    U16(u16),
390    /// Unsigned 32-bit integer with optimization tracking.
391    U32(U32Node),
392    /// Unsigned 64-bit integer.
393    U64(u64),
394    /// 32-bit floating point with optimization tracking.
395    F32(F32Node),
396    /// 64-bit floating point.
397    F64(f64),
398    /// 2D coordinate (x, y as f32).
399    Coord2d(Coordinates2DNode),
400    /// 3D coordinate (x, y, z as f32).
401    Coord3d(Coordinates3DNode),
402    /// UTF-16 encoded string (stored as index, resolved during decode).
403    Utf16(String),
404    /// ASCII/UTF-8 encoded string (stored as index, resolved during decode).
405    Ascii(String),
406    /// Angle value stored as i16.
407    Angle(i16),
408
409    // Unknown/undocumented types - preserved for round-trip encoding fidelity
410    /// Unknown type 0x21 storing a u32 value.
411    Unknown21(u32),
412    /// Unknown type 0x23 storing a u8 value.
413    Unknown23(u8),
414    /// Unknown type 0x24 storing a u16 value.
415    Unknown24(u16),
416    /// Unknown type 0x25 storing a u32 value.
417    Unknown25(u32),
418    /// Unknown type 0x26 with variable-length data (Three Kingdoms DLC).
419    Unknown26(Vec<u8>),
420
421    // Array types
422    /// Array of boolean values.
423    BoolArray(Vec<bool>),
424    /// Array of i8 values (stored as u8 for efficiency).
425    I8Array(Vec<u8>),
426    /// Array of i16 values.
427    I16Array(Vec<i16>),
428    /// Array of i32 values with optimization tracking.
429    I32Array(VecI32Node),
430    /// Array of i64 values.
431    I64Array(Vec<i64>),
432    /// Array of u8 values (raw bytes).
433    U8Array(Vec<u8>),
434    /// Array of u16 values.
435    U16Array(Vec<u16>),
436    /// Array of u32 values with optimization tracking.
437    U32Array(VecU32Node),
438    /// Array of u64 values.
439    U64Array(Vec<u64>),
440    /// Array of f32 values.
441    F32Array(Vec<f32>),
442    /// Array of f64 values.
443    F64Array(Vec<f64>),
444    /// Array of 2D coordinates.
445    Coord2dArray(Vec<Coordinates2DNode>),
446    /// Array of 3D coordinates.
447    Coord3dArray(Vec<Coordinates3DNode>),
448    /// Array of UTF-16 strings.
449    Utf16Array(Vec<String>),
450    /// Array of ASCII strings.
451    AsciiArray(Vec<String>),
452    /// Array of angle values.
453    AngleArray(Vec<i16>),
454
455    /// Record node - a named container holding child nodes.
456    /// Records form the hierarchical structure of ESF files.
457    Record(Box<RecordNode>),
458}
459
460/// Boolean node with optimization tracking.
461///
462/// When `optimized` is true, the value is encoded using `BOOL_TRUE` (0x12) or
463/// `BOOL_FALSE` (0x13) markers which require no additional bytes. When false,
464/// uses the standard `BOOL` (0x01) marker followed by a byte.
465#[derive(Getters, MutGetters, Setters, Eq, PartialEq, Clone, Debug, Default, Serialize, Deserialize)]
466#[getset(get = "pub", get_mut = "pub", set = "pub")]
467pub struct BoolNode {
468    /// The boolean value.
469    value: bool,
470    /// Whether to use optimized encoding (single marker byte, no value byte).
471    optimized: bool,
472}
473
474/// Signed 32-bit integer node with optimization tracking.
475///
476/// When `optimized` is true, the encoder selects the smallest encoding that fits:
477/// - `I32_ZERO` (0x19): Value is 0, no additional bytes
478/// - `I32_BYTE` (0x1a): Value fits in i8 (-128 to 127), 1 byte
479/// - `I32_16BIT` (0x1b): Value fits in i16 (-32768 to 32767), 2 bytes
480/// - `I32_24BIT` (0x1c): Value fits in 24 bits, 3 bytes
481/// - `I32` (0x04): Full 4-byte encoding
482#[derive(Getters, MutGetters, Setters, Eq, PartialEq, Clone, Debug, Default, Serialize, Deserialize)]
483#[getset(get = "pub", get_mut = "pub", set = "pub")]
484pub struct I32Node {
485    /// The signed 32-bit integer value.
486    value: i32,
487    /// Whether to use optimized (variable-width) encoding.
488    optimized: bool,
489}
490
491/// Unsigned 32-bit integer node with optimization tracking.
492///
493/// When `optimized` is true, the encoder selects the smallest encoding that fits:
494/// - `U32_ZERO` (0x14): Value is 0, no additional bytes
495/// - `U32_ONE` (0x15): Value is 1, no additional bytes
496/// - `U32_BYTE` (0x16): Value fits in u8 (0-255), 1 byte
497/// - `U32_16BIT` (0x17): Value fits in u16 (0-65535), 2 bytes
498/// - `U32_24BIT` (0x18): Value fits in 24 bits (0-16777215), 3 bytes
499/// - `U32` (0x08): Full 4-byte encoding
500#[derive(Getters, MutGetters, Setters, Eq, PartialEq, Clone, Debug, Default, Serialize, Deserialize)]
501#[getset(get = "pub", get_mut = "pub", set = "pub")]
502pub struct U32Node {
503    /// The unsigned 32-bit integer value.
504    value: u32,
505    /// Whether to use optimized (variable-width) encoding.
506    optimized: bool,
507}
508
509/// 32-bit floating point node with optimization tracking.
510///
511/// When `optimized` is true and the value is exactly 0.0, uses `F32_ZERO` (0x1d)
512/// which requires no additional bytes. Otherwise uses standard `F32` (0x0a) encoding.
513#[derive(Getters, MutGetters, Setters, PartialEq, Clone, Debug, Default, Serialize, Deserialize)]
514#[getset(get = "pub", get_mut = "pub", set = "pub")]
515pub struct F32Node {
516    /// The 32-bit floating point value.
517    value: f32,
518    /// Whether to use optimized encoding (zero detection).
519    optimized: bool,
520}
521
522/// Array of signed 32-bit integers with optimization tracking.
523///
524/// When `optimized` is true, the encoder analyzes all values to find the smallest
525/// encoding that fits all elements, using byte/16-bit/24-bit arrays when possible.
526#[derive(Getters, MutGetters, Setters, Eq, PartialEq, Clone, Debug, Default, Serialize, Deserialize)]
527#[getset(get = "pub", get_mut = "pub", set = "pub")]
528pub struct VecI32Node {
529    /// The array of i32 values.
530    value: Vec<i32>,
531    /// Whether to use optimized (smaller element size) encoding when possible.
532    optimized: bool,
533}
534
535/// Array of unsigned 32-bit integers with optimization tracking.
536///
537/// When `optimized` is true, the encoder analyzes all values to find the smallest
538/// encoding that fits all elements, using byte/16-bit/24-bit arrays when possible.
539#[derive(Getters, MutGetters, Setters, Eq, PartialEq, Clone, Debug, Default, Serialize, Deserialize)]
540#[getset(get = "pub", get_mut = "pub", set = "pub")]
541pub struct VecU32Node {
542    /// The array of u32 values.
543    value: Vec<u32>,
544    /// Whether to use optimized (smaller element size) encoding when possible.
545    optimized: bool,
546}
547
548/// 2D coordinate node storing X and Y position values.
549///
550/// Commonly used for map positions, UI coordinates, and other 2D spatial data.
551/// Each coordinate is stored as a 32-bit float.
552#[derive(Getters, MutGetters, Setters, PartialEq, Clone, Default, Debug, Serialize, Deserialize)]
553#[getset(get = "pub", get_mut = "pub", set = "pub")]
554pub struct Coordinates2DNode {
555    /// X coordinate.
556    x: f32,
557    /// Y coordinate.
558    y: f32,
559}
560
561/// 3D coordinate node storing X, Y, and Z position values.
562///
563/// Commonly used for world positions, unit locations, and other 3D spatial data.
564/// Each coordinate is stored as a 32-bit float.
565#[derive(Getters, MutGetters, Setters, PartialEq, Clone, Default, Debug, Serialize, Deserialize)]
566#[getset(get = "pub", get_mut = "pub", set = "pub")]
567pub struct Coordinates3DNode {
568    /// X coordinate.
569    x: f32,
570    /// Y coordinate.
571    y: f32,
572    /// Z coordinate.
573    z: f32,
574}
575
576/// A record node that contains other nodes as children.
577///
578/// Record nodes are the structural backbone of ESF files, organizing data into
579/// a hierarchical tree. Each record has a name (referencing the string table),
580/// a version number, and zero or more child nodes.
581///
582/// # Children Organization
583///
584/// Children can be organized in two ways depending on the `HAS_NESTED_BLOCKS` flag:
585/// - **Without nested blocks**: Single flat list of children (`children[0]`)
586/// - **With nested blocks**: Multiple groups of children, each group representing
587///   a logical grouping (e.g., multiple instances of the same record type)
588///
589/// # Example Structure
590///
591/// A typical ESF might have a structure like:
592/// ```text
593/// ROOT
594/// ├── CAMPAIGN_SAVE_GAME (record)
595/// │   ├── FACTION (record, nested blocks for multiple factions)
596/// │   │   ├── [Group 0: Faction 1 data...]
597/// │   │   └── [Group 1: Faction 2 data...]
598/// │   └── DATE (record)
599/// │       ├── year (u32)
600/// │       └── turn (u32)
601/// ```
602#[derive(Getters, MutGetters, Setters, PartialEq, Clone, Default, Debug, Serialize, Deserialize)]
603#[getset(get = "pub", get_mut = "pub", set = "pub")]
604pub struct RecordNode {
605
606    /// Encoding flags controlling how this record is serialized.
607    record_flags: RecordNodeFlags,
608
609    /// Version number of this record's schema (0-255).
610    /// Used by the game to handle format changes across patches.
611    version: u8,
612
613    /// Name of the record (e.g., "FACTION", "CAMPAIGN_SAVE_GAME").
614    /// This is resolved from the record names string table during decode.
615    name: String,
616
617    /// Child nodes organized into groups.
618    /// - Without `HAS_NESTED_BLOCKS`: Single group at index 0
619    /// - With `HAS_NESTED_BLOCKS`: Multiple groups, each a separate logical unit
620    children: Vec<Vec<NodeType>>
621}
622
623//---------------------------------------------------------------------------//
624//                           Implementation of ESF
625//---------------------------------------------------------------------------//
626
627impl ESF {
628
629    /// Creates a shallow copy of this ESF with the root node replaced by `Invalid`.
630    ///
631    /// This is useful when you need to preserve the header metadata (signature,
632    /// creation date, etc.) but want to rebuild or replace the node tree.
633    pub fn clone_without_root_node(&self) -> Self {
634        Self {
635            signature: self.signature,
636            unknown_1: self.unknown_1,
637            creation_date: self.creation_date,
638            root_node: NodeType::Invalid,
639            strings_in_index_order: self.strings_in_index_order,
640        }
641    }
642}
643
644impl NodeType {
645
646    /// Creates a copy of a node without its children (for record nodes).
647    ///
648    /// For record nodes, this creates a new record with the same name, flags,
649    /// and version but with an empty children list. For all other node types,
650    /// this is equivalent to `clone()`.
651    ///
652    /// Useful for building modified node trees while preserving the structure.
653    pub fn clone_without_children(&self) -> Self {
654        match self {
655            Self::Record(node) => {
656                let mut new_node = RecordNode::default();
657                new_node.set_name(node.name().to_owned());
658                new_node.set_record_flags(*node.record_flags());
659                new_node.set_version(*node.version());
660
661                Self::Record(Box::new(new_node))
662            }
663
664            _ => self.clone()
665        }
666    }
667}
668
669impl Display for ESFSignature {
670    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
671        Display::fmt(match self {
672            Self::CAAB => "CAAB",
673            Self::CBAB => "CBAB",
674            Self::CEAB => "CEAB",
675            Self::CFAB => "CFAB",
676        }, f)
677    }
678}
679
680impl TryFrom<&str> for ESFSignature {
681    type Error = RLibError;
682
683    fn try_from(value: &str) -> Result<Self, Self::Error> {
684        match value {
685            "CAAB" => Ok(Self::CAAB),
686            "CBAB" => Ok(Self::CBAB),
687            "CEAB" => Ok(Self::CEAB),
688            "CFAB" => Ok(Self::CFAB),
689            _ => Err(RLibError::UnknownESFSignature(value.to_string())),
690        }
691    }
692}
693
694impl TryFrom<Vec<u8>> for ESFSignature {
695    type Error = RLibError;
696
697    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
698        match value.as_slice().try_into()? {
699            SIGNATURE_CAAB => Ok(Self::CAAB),
700            SIGNATURE_CBAB => Ok(Self::CBAB),
701            SIGNATURE_CEAB => Ok(Self::CEAB),
702            SIGNATURE_CFAB => Ok(Self::CFAB),
703            _ => Err(RLibError::UnknownESFSignatureBytes(value[0], value[1])),
704        }
705    }
706}
707
708impl From<ESFSignature> for Vec<u8> {
709    fn from(value: ESFSignature) -> Self {
710        match value {
711            ESFSignature::CAAB => SIGNATURE_CAAB.to_vec(),
712            ESFSignature::CBAB => SIGNATURE_CBAB.to_vec(),
713            ESFSignature::CEAB => SIGNATURE_CEAB.to_vec(),
714            ESFSignature::CFAB => SIGNATURE_CFAB.to_vec(),
715        }
716    }
717}
718
719impl Decodeable for ESF {
720
721    fn decode<R: ReadBytes>(data: &mut R, _extra_data: &Option<DecodeableExtraData>) -> Result<Self> {
722        let mut esf = Self::default();
723
724        let sig_bytes = data.read_slice(4, false)?;
725        esf.signature = ESFSignature::try_from(sig_bytes.to_vec())?;
726
727        match esf.signature {
728            ESFSignature::CAAB => Self::read_caab(&mut esf, data)?,
729            ESFSignature::CBAB => Self::read_cbab(&mut esf, data)?,
730            _ => return Err(RLibError::DecodingESFUnsupportedSignature(sig_bytes[0], sig_bytes[1])),
731        };
732
733        // Debugging code.
734        //use std::io::Write;
735        //let mut x = std::fs::File::create("ceo.json")?;
736        //x.write_all(&serde_json::to_string_pretty(&esf).unwrap().as_bytes())?;
737
738        Ok(esf)
739    }
740}
741
742impl Encodeable for ESF {
743
744    fn encode<W: WriteBytes>(&mut self, buffer: &mut W, extra_data: &Option<EncodeableExtraData>) -> Result<()> {
745        let sig_bytes: Vec<u8> = Vec::from(self.signature);
746        buffer.write_all(&sig_bytes)?;
747
748        match self.signature {
749            ESFSignature::CAAB => self.save_caab(buffer, extra_data),
750            ESFSignature::CBAB => self.save_cbab(buffer, extra_data),
751            _ => Err(RLibError::EncodingESFUnsupportedSignature(self.signature.to_string())),
752        }
753    }
754}