Skip to main content

rpfm_lib/files/esf/
utils.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//! Shared utility functions for ESF node reading and writing.
12//!
13//! This module contains format-agnostic functions used by both CAAB and CBAB
14//! implementations. The primary functions handle recursive node tree traversal
15//! for both decoding and encoding operations.
16//!
17//! # Key Functions
18//!
19//! - [`ESF::read_node`]: Recursively decodes a node and all its children from binary data
20//! - [`ESF::save_node`]: Recursively encodes a node and all its children to binary data
21//! - [`ESF::read_string_from_node`]: Collects all strings from a node tree for string table generation
22
23use std::collections::BTreeMap;
24use std::io::{SeekFrom, Write};
25
26use crate::binary::{ReadBytes, WriteBytes};
27use crate::error::{RLibError, Result};
28
29use super::*;
30use super::stream::EsfStream;
31
32#[cfg(test)] #[path = "utils_test.rs"] mod utils_test;
33
34//---------------------------------------------------------------------------//
35//                                  Functions
36//---------------------------------------------------------------------------//
37
38impl ESF {
39
40    /// Recursively decodes a node and all its children from binary ESF data.
41    ///
42    /// This is the core parsing function that handles all 40+ node types in the ESF format.
43    /// It reads the type marker byte and dispatches to the appropriate decoding logic.
44    ///
45    /// # Arguments
46    ///
47    /// * `data` - Reader positioned at the start of a node
48    /// * `is_root_node` - True if this is the root node (affects record header encoding)
49    /// * `record_names` - Pre-loaded table of record names for index lookup
50    /// * `strings_utf8` - Pre-loaded table of UTF-8 strings for index lookup
51    /// * `strings_utf16` - Pre-loaded table of UTF-16 strings for index lookup
52    ///
53    /// # Node Type Detection
54    ///
55    /// The first byte determines the node type:
56    /// - `0x80+`: Record node (high bit set indicates record)
57    /// - `0x01-0x10`: Primitive types
58    /// - `0x12-0x1d`: Optimized primitives
59    /// - `0x21-0x26`: Unknown types
60    /// - `0x41-0x50`: Arrays
61    /// - `0x52-0x5d`: Optimized arrays
62    ///
63    /// # Record Node Parsing
64    ///
65    /// Record nodes have complex header encoding:
66    /// - Root nodes and nodes with `HAS_NON_OPTIMIZED_INFO` use 3-byte headers
67    /// - Other nodes use 2-byte optimized headers with packed version/name index
68    /// - Block sizes are CAULEB128-encoded
69    /// - Nested blocks have individual size prefixes per group
70    ///
71    /// # Errors
72    ///
73    /// Returns an error if:
74    /// - Unknown node type marker encountered
75    /// - String/record name index out of bounds
76    /// - Block size mismatch (didn't consume expected bytes)
77    pub(crate) fn read_node<R: ReadBytes>(
78        data: &mut R,
79        is_root_node: bool,
80        record_names: &[String],
81        strings_utf8: &BTreeMap<u32, String>,
82        strings_utf16: &BTreeMap<u32, String>
83    ) -> Result<NodeType> {
84
85        let next_byte = data.read_u8()?;
86        let is_record = next_byte & RecordNodeFlags::IS_RECORD_NODE.bits() == RecordNodeFlags::IS_RECORD_NODE.bits();
87
88        // Get the node type. If it's a record, process it separately from the rest, as records are significantly more complex than standard nodes.
89        let node_type = if is_record {
90
91            // Get the record flags, and decode it depending on what flags it has.
92            let record_flags = RecordNodeFlags::from_bits_truncate(next_byte);
93            let has_non_optimized_info = record_flags.contains(RecordNodeFlags::HAS_NON_OPTIMIZED_INFO) || is_root_node;
94            let name_index;
95            let version;
96
97            if has_non_optimized_info {
98                name_index = data.read_u16()?;
99                version = data.read_u8()?;
100            }
101
102            // If it's not the root node, the data is encoded in 2 bytes using bitwise.
103            // From left to right:
104            // - 0..=2: Flags.
105            // - 3..6: Version.
106            // - 7..16: Name index.
107            else {
108                version = (next_byte & 0x1E) >> 1;
109                name_index = (((next_byte & 1) as u16) << 8) + data.read_u8()? as u16;
110            }
111
112            let name = match record_names.get(name_index as usize) {
113                Some(name) => name.to_owned(),
114                None => return Err(RLibError::DecodingESFRecordNameNotFound(name_index)),
115            };
116
117            // Get the block size, to know what data do we have to decode exactly.
118            let block_size = data.read_cauleb128()?;
119            let group_count = if record_flags.contains(RecordNodeFlags::HAS_NESTED_BLOCKS) {
120                data.read_cauleb128()?
121            } else { 1 };
122
123            let final_block_offset = data.stream_position()? as usize + block_size as usize;
124            let mut children = Vec::with_capacity(group_count as usize);
125
126            // Get the record data. This process differs depending if we have nested blocks or not.
127            // If we have nested blocks, we decode group by group. If we don't, we just treat it as a single group.
128            for _ in 0..group_count {
129                let final_entry_offset = if record_flags.contains(RecordNodeFlags::HAS_NESTED_BLOCKS) {
130                    let entry_size = data.read_cauleb128()?;
131                    data.stream_position()? as usize + entry_size as usize
132                } else {
133                    final_block_offset
134                };
135
136                let mut node_list = vec![];
137                while data.stream_position()? < final_entry_offset as u64 {
138                    node_list.push(Self::read_node(data, false, record_names, strings_utf8, strings_utf16)?);
139                }
140
141                // Make sure we decoded exactly the data we wanted.
142                let curr_pos = data.stream_position()?;
143                if curr_pos != final_entry_offset as u64 {
144                    return Err(RLibError::DecodingMismatchSizeError(final_entry_offset, curr_pos as usize));
145                }
146
147                children.push(node_list);
148            }
149
150            // Make sure we decoded exactly the data we wanted.
151            let curr_pos = data.stream_position()?;
152            if curr_pos != final_block_offset as u64 {
153                return Err(RLibError::DecodingMismatchSizeError(final_block_offset, curr_pos as usize));
154            }
155
156            let node_data = RecordNode {
157                record_flags,
158                version,
159                name,
160                children,
161            };
162
163            NodeType::Record(Box::new(node_data))
164        }
165
166        // If its not a record node, get the type from the type byte and decode it.
167        else {
168            match next_byte {
169
170                // Invalid node. This is always an error.
171                INVALID => return Err(RLibError::DecodingESFUnsupportedDataType(next_byte)),
172
173                //------------------------------------------------//
174                // Primitive nodes.
175                //------------------------------------------------//
176                BOOL => NodeType::Bool(BoolNode {
177                    value: data.read_bool()?,
178                    optimized: false,
179                }),
180                I8 => NodeType::I8(data.read_i8()?),
181                I16 => NodeType::I16(data.read_i16()?),
182                I32 => NodeType::I32(I32Node {
183                    value: data.read_i32()?,
184                    optimized: false,
185                }),
186                I64 => NodeType::I64(data.read_i64()?),
187                U8 => NodeType::U8(data.read_u8()?),
188                U16 => NodeType::U16(data.read_u16()?),
189                U32 => NodeType::U32(U32Node {
190                    value: data.read_u32()?,
191                    optimized: false,
192                }),
193                U64 => NodeType::U64(data.read_u64()?),
194                F32 => NodeType::F32(F32Node {
195                    value: data.read_f32()?,
196                    optimized: false,
197                }),
198                F64 => NodeType::F64(data.read_f64()?),
199
200                //------------------------------------------------//
201                // Complex/Specialized nodes.
202                //------------------------------------------------//
203                COORD_2D =>{
204                    NodeType::Coord2d(Coordinates2DNode{
205                        x: data.read_f32()?,
206                        y: data.read_f32()?
207                    })
208                },
209                COORD_3D =>{
210                    NodeType::Coord3d(Coordinates3DNode{
211                        x: data.read_f32()?,
212                        y: data.read_f32()?,
213                        z: data.read_f32()?
214                    })
215                },
216
217                //------------------------------------------------//
218                // String nodes.
219                //------------------------------------------------//
220                UTF16 => {
221                    let string_index = data.read_u32()?;
222                    match strings_utf16.get(&string_index) {
223                        Some(string) => NodeType::Utf16(string.to_owned()),
224                        None => return Err(RLibError::DecodingESFStringNotFound(string_index)),
225                    }
226                },
227                ASCII => {
228                    let string_index = data.read_u32()?;
229                    match strings_utf8.get(&string_index) {
230                        Some(string) => NodeType::Ascii(string.to_owned()),
231                        None => return Err(RLibError::DecodingESFStringNotFound(string_index)),
232                    }
233                },
234                ANGLE => NodeType::Angle(data.read_i16()?),
235
236                //------------------------------------------------//
237                // Optimized primitive nodes.
238                //------------------------------------------------//
239                BOOL_TRUE => NodeType::Bool(BoolNode {
240                    value: true,
241                    optimized: true,
242                }),
243                BOOL_FALSE => NodeType::Bool(BoolNode {
244                    value: false,
245                    optimized: true,
246                }),
247                U32_ZERO => NodeType::U32(U32Node {
248                    value: 0,
249                    optimized: true,
250                }),
251                U32_ONE => NodeType::U32(U32Node {
252                    value: 1,
253                    optimized: true,
254                }),
255                U32_BYTE => NodeType::U32(U32Node {
256                    value: data.read_u8()? as u32,
257                    optimized: true,
258                }),
259                U32_16BIT => NodeType::U32(U32Node {
260                    value: data.read_u16()? as u32,
261                    optimized: true,
262                }),
263                U32_24BIT => NodeType::U32(U32Node {
264                    value: data.read_u24_be()?,
265                    optimized: true,
266                }),
267                I32_ZERO => NodeType::I32(I32Node {
268                    value: 0,
269                    optimized: true,
270                }),
271                I32_BYTE => NodeType::I32(I32Node {
272                    value: data.read_i8()? as i32,
273                    optimized: true,
274                }),
275                I32_16BIT => NodeType::I32(I32Node {
276                    value: data.read_i16()? as i32,
277                    optimized: true,
278                }),
279                I32_24BIT => NodeType::I32(I32Node {
280                    value: data.read_i24_be()?,
281                    optimized: true,
282                }),
283                F32_ZERO => NodeType::F32(F32Node {
284                    value: 0.0,
285                    optimized: true,
286                }),
287
288                //------------------------------------------------//
289                // Unknown nodes.
290                //------------------------------------------------//
291                UNKNOWN_21 => NodeType::Unknown21(data.read_u32()?),
292                UNKNOWN_23 => NodeType::Unknown23(data.read_u8()?),
293                UNKNOWN_24 => NodeType::Unknown24(data.read_u16()?),
294                UNKNOWN_25 => NodeType::Unknown25(data.read_u32()?),
295
296                // Very weird type.
297                UNKNOWN_26 => {
298                    let mut node_data = vec![];
299                    let first_byte = data.read_u8()?;
300                    node_data.push(first_byte);
301
302                    if first_byte % 8 == 0 && first_byte != 0 {
303                        node_data.extend_from_slice(&data.read_slice(first_byte as usize, false)?);
304                    } else {
305                        node_data.extend_from_slice(&data.read_slice(7, false)?);
306                    }
307
308                    let last_byte = data.read_u8();
309                    if last_byte.is_ok() && last_byte.unwrap() != 0x9C {
310                        data.seek(SeekFrom::Current(-1))?;
311                    }
312
313                    NodeType::Unknown26(node_data)
314                },
315
316                //------------------------------------------------//
317                // Arrays of primitive nodes.
318                //------------------------------------------------//
319                BOOL_ARRAY => {
320                    let mut node_data = vec![];
321                    let size = data.read_cauleb128()?;
322                    let end_offset = data.stream_position()? + size as u64;
323
324                    while data.stream_position()? < end_offset {
325                        node_data.push(data.read_bool()?);
326                    }
327
328                    NodeType::BoolArray(node_data)
329                },
330
331                I8_ARRAY => {
332                    let size = data.read_cauleb128()?;
333                    let node_data = data.read_slice(size as usize, false)?;
334
335                    NodeType::I8Array(node_data)
336                },
337
338                I16_ARRAY => {
339                    let mut node_data = vec![];
340                    let size = data.read_cauleb128()?;
341                    let end_offset = data.stream_position()? + size as u64;
342
343                    while data.stream_position()? < end_offset {
344                        node_data.push(data.read_i16()?)
345                    }
346                    NodeType::I16Array(node_data)
347                },
348
349                I32_ARRAY => {
350                    let mut node_data = vec![];
351                    let size = data.read_cauleb128()?;
352                    let end_offset = data.stream_position()? + size as u64;
353
354                    while data.stream_position()? < end_offset {
355                        node_data.push(data.read_i32()?);
356                    }
357
358                    NodeType::I32Array(VecI32Node {
359                        value: node_data,
360                        optimized: false,
361                    })
362                },
363
364                I64_ARRAY => {
365                    let mut node_data = vec![];
366                    let size = data.read_cauleb128()?;
367                    let end_offset = data.stream_position()? + size as u64;
368
369                    while data.stream_position()? < end_offset {
370                        node_data.push(data.read_i64()?);
371                    }
372
373                    NodeType::I64Array(node_data)
374                },
375
376                U8_ARRAY => {
377                    let size = data.read_cauleb128()?;
378                    let node_data = data.read_slice(size as usize, false)?;
379
380                    NodeType::U8Array(node_data)
381                },
382
383                U16_ARRAY => {
384                    let mut node_data = vec![];
385                    let size = data.read_cauleb128()?;
386                    let end_offset = data.stream_position()? + size as u64;
387
388                    while data.stream_position()? < end_offset {
389                        node_data.push(data.read_u16()?);
390                    }
391
392                    NodeType::U16Array(node_data)
393                },
394
395                U32_ARRAY => {
396                    let mut node_data = vec![];
397                    let size = data.read_cauleb128()?;
398                    let end_offset = data.stream_position()? + size as u64;
399
400                    while data.stream_position()? < end_offset {
401                        node_data.push(data.read_u32()?);
402                    }
403
404                    NodeType::U32Array(VecU32Node {
405                        value: node_data,
406                        optimized: false,
407                    })
408                },
409
410                U64_ARRAY => {
411                    let mut node_data = vec![];
412                    let size = data.read_cauleb128()?;
413                    let end_offset = data.stream_position()? + size as u64;
414
415                    while data.stream_position()? < end_offset {
416                        node_data.push(data.read_u64()?)
417                    }
418
419                    NodeType::U64Array(node_data)
420                },
421
422                F32_ARRAY => {
423                    let mut node_data = vec![];
424                    let size = data.read_cauleb128()?;
425                    let end_offset = data.stream_position()? + size as u64;
426
427                    while data.stream_position()? < end_offset {
428                        node_data.push(data.read_f32()?);
429                    }
430
431                    NodeType::F32Array(node_data)
432                },
433
434                F64_ARRAY => {
435                    let mut node_data = vec![];
436                    let size = data.read_cauleb128()?;
437                    let end_offset = data.stream_position()? + size as u64;
438
439                    while data.stream_position()? < end_offset {
440                        node_data.push(data.read_f64()?);
441                    }
442
443                    NodeType::F64Array(node_data)
444                },
445
446                //------------------------------------------------//
447                // Array of complex/specialized nodes.
448                //------------------------------------------------//
449                COORD_2D_ARRAY => {
450                    let mut node_data = vec![];
451                    let size = data.read_cauleb128()?;
452                    let end_offset = data.stream_position()? + size as u64;
453
454                    while data.stream_position()? < end_offset {
455                        node_data.push(Coordinates2DNode{
456                            x: data.read_f32()?,
457                            y: data.read_f32()?
458                        });
459                    }
460
461                    NodeType::Coord2dArray(node_data)
462                },
463
464                COORD_3D_ARRAY => {
465                    let mut node_data = vec![];
466                    let size = data.read_cauleb128()?;
467                    let end_offset = data.stream_position()? + size as u64;
468
469                    while data.stream_position()? < end_offset {
470                        node_data.push(Coordinates3DNode{
471                            x: data.read_f32()?,
472                            y: data.read_f32()?,
473                            z: data.read_f32()?
474                        });
475                    }
476
477                    NodeType::Coord3dArray(node_data)
478                },
479
480                //------------------------------------------------//
481                // Array of string nodes.
482                //------------------------------------------------//
483                UTF16_ARRAY => {
484                    let mut node_data = vec![];
485                    let size = data.read_cauleb128()?;
486                    let end_offset = data.stream_position()? + size as u64;
487
488                    while data.stream_position()? < end_offset {
489                        let string_index = data.read_u32()?;
490                        match strings_utf16.get(&string_index) {
491                            Some(string) => node_data.push(string.to_owned()),
492                            None => return Err(RLibError::DecodingESFStringNotFound(string_index)),
493                        }
494                    }
495                    NodeType::Utf16Array(node_data)
496                },
497
498                ASCII_ARRAY => {
499                    let mut node_data = vec![];
500                    let size = data.read_cauleb128()?;
501                    let end_offset = data.stream_position()? + size as u64;
502
503                    while data.stream_position()? < end_offset {
504                        let string_index = data.read_u32()?;
505                        match strings_utf8.get(&string_index) {
506                            Some(string) => node_data.push(string.to_owned()),
507                            None => return Err(RLibError::DecodingESFStringNotFound(string_index)),
508                        }
509                    }
510                    NodeType::AsciiArray(node_data)
511                },
512
513                //------------------------------------------------//
514                // Array of unknown nodes.
515                //------------------------------------------------//
516                ANGLE_ARRAY =>{
517                    let mut node_data = vec![];
518                    let size = data.read_cauleb128()?;
519                    let end_offset = data.stream_position()? + size as u64;
520
521                    while data.stream_position()? < end_offset {
522                        node_data.push(data.read_i16()?)
523                    }
524
525                    NodeType::AngleArray(node_data)
526                },
527
528                //------------------------------------------------//
529                // Arrays of optimized primitive nodes.
530                //------------------------------------------------//
531                U32_BYTE_ARRAY => {
532                    let mut node_data = vec![];
533                    let size = data.read_cauleb128()?;
534                    let end_offset = data.stream_position()? + size as u64;
535
536                    while data.stream_position()? < end_offset {
537                        node_data.push(data.read_u8()? as u32);
538                    }
539
540                    NodeType::U32Array(VecU32Node {
541                        value: node_data,
542                        optimized: true,
543                    })
544                },
545
546                U32_16BIT_ARRAY => {
547                    let mut node_data = vec![];
548                    let size = data.read_cauleb128()?;
549                    let end_offset = data.stream_position()? + size as u64;
550
551                    while data.stream_position()? < end_offset {
552                        node_data.push(data.read_u16()? as u32);
553                    }
554
555                    NodeType::U32Array(VecU32Node {
556                        value: node_data,
557                        optimized: true,
558                    })
559                },
560
561                U32_24BIT_ARRAY => {
562                    let mut node_data = vec![];
563                    let size = data.read_cauleb128()?;
564                    let end_offset = data.stream_position()? + size as u64;
565
566                    while data.stream_position()? < end_offset {
567                        node_data.push(data.read_u24_be()?);
568                    }
569
570                    NodeType::U32Array(VecU32Node {
571                        value: node_data,
572                        optimized: true,
573                    })
574                },
575
576                I32_BYTE_ARRAY => {
577                    let mut node_data = vec![];
578                    let size = data.read_cauleb128()?;
579                    let end_offset = data.stream_position()? + size as u64;
580
581                    while data.stream_position()? < end_offset {
582                        node_data.push(data.read_i8()? as i32);
583                    }
584
585                    NodeType::I32Array(VecI32Node {
586                        value: node_data,
587                        optimized: true,
588                    })
589                },
590
591                I32_16BIT_ARRAY => {
592                    let mut node_data = vec![];
593                    let size = data.read_cauleb128()?;
594                    let end_offset = data.stream_position()? + size as u64;
595
596                    while data.stream_position()? < end_offset {
597                        node_data.push(data.read_i16()? as i32);
598                    }
599
600                    NodeType::I32Array(VecI32Node {
601                        value: node_data,
602                        optimized: true,
603                    })
604                },
605
606                I32_24BIT_ARRAY => {
607                    let mut node_data = vec![];
608                    let size = data.read_cauleb128()?;
609                    let end_offset = data.stream_position()? + size as u64;
610
611                    while data.stream_position()? < end_offset {
612                        node_data.push(data.read_i24_be()?);
613                    }
614
615                    NodeType::I32Array(VecI32Node {
616                        value: node_data,
617                        optimized: true,
618                    })
619                },
620
621                // Anything else is not yet supported.
622                _ => return Err(RLibError::DecodingESFUnsupportedDataType(next_byte)),
623            }
624        };
625
626        // Debugging code: re-save every slot and compare it with it's source data.
627        // To check for read/save integrity.
628        //if *offset > 1040000 {
629        //    let data = Self::save_node(&node_type, is_root_node, record_names, &strings_utf8.values().map(|x| x.to_owned()).collect::<Vec<String>>(), &strings_utf16.values().map(|x| x.to_owned()).collect::<Vec<String>>());
630        //    if data != data[initial_offset..*offset] {
631        //        dbg!(next_byte);
632        //        dbg!(*offset);
633        //        let max = if data.len() > 20 { 20 } else { data.len() };
634        //        dbg!(&data[..max]);
635        //        dbg!(&data[initial_offset..(initial_offset + max)]);
636        //        //return Err(ErrorKind::ESFUnsupportedDataType(format!("{}", next_byte)).into());
637        //    }
638        //}
639
640        Ok(node_type)
641    }
642
643    /// Recursively encodes a node and all its children to binary ESF data.
644    ///
645    /// This is the core encoding function that handles all node types, applying
646    /// appropriate optimizations based on the node's `optimized` flag where applicable.
647    ///
648    /// # Arguments
649    ///
650    /// * `buffer` - Output buffer to write encoded data to
651    /// * `node_type` - The node to encode
652    /// * `is_root_node` - True if this is the root node (affects record header encoding)
653    /// * `record_names` - String table for record name index lookup
654    /// * `strings_utf8` - String table for UTF-8 string index lookup
655    /// * `strings_utf16` - String table for UTF-16 string index lookup
656    ///
657    /// # Optimization Behavior
658    ///
659    /// For node types with optimization tracking (`BoolNode`, `I32Node`, `U32Node`, etc.):
660    /// - If `optimized` is true, selects the smallest encoding that fits the value
661    /// - If `optimized` is false, uses the standard full-size encoding
662    ///
663    /// This preserves encoding fidelity during round-trip operations.
664    ///
665    /// # Record Node Encoding
666    ///
667    /// Record nodes use different header formats:
668    /// - Root nodes: Always use 3-byte headers (flags + u16 name index + u8 version)
669    /// - Non-root with `HAS_NON_OPTIMIZED_INFO`: Same 3-byte header
670    /// - Non-root optimized: 2-byte packed header
671    ///
672    /// # Errors
673    ///
674    /// Returns an error if write operations fail or string lookups fail.
675    pub(crate) fn save_node(buffer: &mut EsfStream, node_type: &NodeType, is_root_node: bool, record_names: &[String], strings_utf8: &[String], strings_utf16: &[String]) -> Result<()> {
676        match node_type {
677
678            // Crash with this for now.
679            NodeType::Invalid => unimplemented!(),
680
681            //------------------------------------------------//
682            // Primitive nodes.
683            //------------------------------------------------//
684            NodeType::Bool(value) => {
685                if *value.optimized() {
686                    if *value.value() {
687                        buffer.write_u8(BOOL_TRUE)?;
688                    } else {
689                        buffer.write_u8(BOOL_FALSE)?;
690                    }
691                } else {
692                    buffer.write_u8(BOOL)?;
693                    buffer.write_bool(*value.value())?;
694                }
695            },
696            NodeType::I8(value) => {
697                buffer.write_u8(I8)?;
698                buffer.write_i8(*value)?;
699            },
700            NodeType::I16(value) => {
701                buffer.write_u8(I16)?;
702                buffer.write_i16(*value)?;
703            },
704            NodeType::I32(value) => {
705                if *value.optimized() {
706                    let value = *value.value();
707                    if value == 0 {
708                        buffer.write_u8(I32_ZERO)?;
709                    }
710
711                    // We can do simple logic for positive numbers, but negative numbers need special logic to get their size correctly.
712                    else if value.is_positive() {
713                        if value <= i8::MAX as i32 {
714                            buffer.write_u8(I32_BYTE)?;
715                            buffer.write_i8(value as i8)?;
716                        } else if value <= i16::MAX as i32 {
717                            buffer.write_u8(I32_16BIT)?;
718                            buffer.write_i16(value as i16)?;
719                        } else if value <= 8_388_607 {
720                            buffer.write_u8(I32_24BIT)?;
721                            buffer.write_i24_be(value)?;
722                        } else {
723                            buffer.write_u8(I32)?;
724                            buffer.write_i32(value)?;
725                        }
726                    } else if value >= i8::MIN as i32 {
727                        buffer.write_u8(I32_BYTE)?;
728                        buffer.write_i8(value as i8)?;
729                    } else if value >= i16::MIN as i32 {
730                        buffer.write_u8(I32_16BIT)?;
731                        buffer.write_i16(value as i16)?;
732                    } else if value >= -8_388_608 {
733                        buffer.write_u8(I32_24BIT)?;
734                        buffer.write_i24_be(value)?;
735                    } else {
736                        buffer.write_u8(I32)?;
737                        buffer.write_i32(value)?;
738                    }
739                } else {
740                    buffer.write_u8(I32)?;
741                    buffer.write_i32(*value.value())?;
742                }
743            },
744            NodeType::I64(value) => {
745                buffer.write_u8(I64)?;
746                buffer.write_i64(*value)?;
747            },
748            NodeType::U8(value) => {
749                buffer.write_u8(U8)?;
750                buffer.write_u8(*value)?;
751            },
752            NodeType::U16(value) => {
753                buffer.write_u8(U16)?;
754                buffer.write_u16(*value)?;
755            },
756            NodeType::U32(value) => {
757                if *value.optimized() {
758                    let value = *value.value();
759                    if value == 0 {
760                        buffer.write_u8(U32_ZERO)?;
761                    } else if value == 1 {
762                        buffer.write_u8(U32_ONE)?;
763                    } else if value <= 0xFF {
764                        buffer.write_u8(U32_BYTE)?;
765                        buffer.write_u8(value as u8)?;
766                    } else if value <= 0xFFFF {
767                        buffer.write_u8(U32_16BIT)?;
768                        buffer.write_u16(value as u16)?;
769                    } else if value <= 0xFFFFFF {
770                        buffer.write_u8(U32_24BIT)?;
771                        buffer.write_u24_be(value)?;
772                    } else {
773                        buffer.write_u8(U32)?;
774                        buffer.write_u32(value)?;
775                    }
776                } else {
777                    buffer.write_u8(U32)?;
778                    buffer.write_u32(*value.value())?;
779                }
780            },
781            NodeType::U64(value) => {
782                buffer.write_u8(U64)?;
783                buffer.write_u64(*value)?;
784            },
785            NodeType::F32(value) => {
786                if *value.optimized() {
787                    let value = *value.value();
788                    if (value - 0.0).abs() < f32::EPSILON {
789                        buffer.write_u8(F32_ZERO)?;
790                    } else {
791                        buffer.write_u8(F32)?;
792                        buffer.write_f32(value)?;
793                    }
794                } else {
795                    buffer.write_u8(F32)?;
796                    buffer.write_f32(*value.value())?;
797                }
798            },
799            NodeType::F64(value) => {
800                buffer.write_u8(F64)?;
801                buffer.write_f64(*value)?;
802            },
803
804            //------------------------------------------------//
805            // Complex nodes.
806            //------------------------------------------------//
807            NodeType::Coord2d(value) => {
808                buffer.write_u8(COORD_2D)?;
809                buffer.write_f32(value.x)?;
810                buffer.write_f32(value.y)?;
811            },
812            NodeType::Coord3d(value) => {
813                buffer.write_u8(COORD_3D)?;
814                buffer.write_f32(value.x)?;
815                buffer.write_f32(value.y)?;
816                buffer.write_f32(value.z)?;
817            },
818
819            //------------------------------------------------//
820            // String nodes.
821            //------------------------------------------------//
822            NodeType::Utf16(value) => {
823                buffer.write_u8(UTF16)?;
824                buffer.write_u32(strings_utf16.iter().position(|x| x == value).unwrap() as u32)?;
825            },
826            NodeType::Ascii(value) => {
827                buffer.write_u8(ASCII)?;
828                buffer.write_u32(strings_utf8.iter().position(|x| x == value).unwrap() as u32)?;
829            },
830            NodeType::Angle(value) => {
831                buffer.write_u8(ANGLE)?;
832                buffer.write_i16(*value)?;
833            },
834
835            //------------------------------------------------//
836            // Unknown nodes.
837            //------------------------------------------------//
838            NodeType::Unknown21(value) => {
839                buffer.write_u8(UNKNOWN_21)?;
840                buffer.write_u32(*value)?;
841            },
842            NodeType::Unknown23(value) => {
843                buffer.write_u8(UNKNOWN_23)?;
844                buffer.write_u8(*value)?;
845            },
846            NodeType::Unknown24(value) => {
847                buffer.write_u8(UNKNOWN_24)?;
848                buffer.write_u16(*value)?;
849
850            },
851            NodeType::Unknown25(value) => {
852                buffer.write_u8(UNKNOWN_25)?;
853                buffer.write_u32(*value)?;
854            },
855            NodeType::Unknown26(value) => {
856                buffer.write_u8(UNKNOWN_26)?;
857                buffer.write_all(value)?;
858            }
859
860            //------------------------------------------------//
861            // Arrays of primitive nodes.
862            //------------------------------------------------//
863            NodeType::BoolArray(value) => {
864                buffer.write_u8(BOOL_ARRAY)?;
865
866                let mut list = vec![];
867                value.iter().try_for_each(|x| list.write_bool(*x))?;
868                buffer.write_sized(&list)?;
869            },
870            NodeType::I8Array(value) => {
871                buffer.write_u8(I8_ARRAY)?;
872
873                let mut list = vec![];
874                value.iter().try_for_each(|x| list.write_i8(*x as i8))?;
875                buffer.write_sized(&list)?;
876            },
877            NodeType::I16Array(value) => {
878                buffer.write_u8(I16_ARRAY)?;
879
880                let mut list = vec![];
881                value.iter().try_for_each(|x| list.write_i16(*x))?;
882                buffer.write_sized(&list)?;
883            },
884            NodeType::I32Array(value) => {
885                let mut list = vec![];
886
887                // Empty arrays have no optimized encoding: the games write them as plain arrays.
888                let largest = value.value().iter().map(|x| x.unsigned_abs()).max();
889                match largest {
890                    Some(largest) if *value.optimized() && largest <= i8::MAX as u32 => {
891                        buffer.write_u8(I32_BYTE_ARRAY)?;
892                        value.value().iter().try_for_each(|x| list.write_i8(*x as i8))?;
893                    },
894                    Some(largest) if *value.optimized() && largest <= i16::MAX as u32 => {
895                        buffer.write_u8(I32_16BIT_ARRAY)?;
896                        value.value().iter().try_for_each(|x| list.write_i16(*x as i16))?;
897                    },
898                    Some(largest) if *value.optimized() && largest <= 8_388_607 => {
899                        buffer.write_u8(I32_24BIT_ARRAY)?;
900                        value.value().iter().try_for_each(|x| list.write_i24_be(*x))?;
901                    },
902                    _ => {
903                        buffer.write_u8(I32_ARRAY)?;
904                        value.value().iter().try_for_each(|x| list.write_i32(*x))?;
905                    },
906                }
907
908                buffer.write_sized(&list)?;
909            },
910
911            NodeType::I64Array(value) => {
912                buffer.write_u8(I64_ARRAY)?;
913
914                let mut list = vec![];
915                value.iter().try_for_each(|x| list.write_i64(*x))?;
916                buffer.write_sized(&list)?;
917            },
918            NodeType::U8Array(value) => {
919                buffer.write_u8(U8_ARRAY)?;
920                buffer.write_sized(value)?;
921            },
922            NodeType::U16Array(value) => {
923                buffer.write_u8(U16_ARRAY)?;
924
925                let mut list = vec![];
926                value.iter().try_for_each(|x| list.write_u16(*x))?;
927                buffer.write_sized(&list)?;
928            },
929            NodeType::U32Array(value) => {
930                let mut list = vec![];
931
932                // Empty arrays have no optimized encoding: the games write them as plain arrays.
933                match value.value().iter().max() {
934                    Some(max_value) if *value.optimized() && max_value < &0xFF => {
935                        buffer.write_u8(U32_BYTE_ARRAY)?;
936                        value.value().iter().for_each(|x| list.push(*x as u8));
937                    },
938                    Some(max_value) if *value.optimized() && max_value < &0xFFFF => {
939                        buffer.write_u8(U32_16BIT_ARRAY)?;
940                        value.value().iter().try_for_each(|x| list.write_u16(*x as u16))?;
941                    },
942                    Some(max_value) if *value.optimized() && max_value < &0xFFFFFF => {
943                        buffer.write_u8(U32_24BIT_ARRAY)?;
944                        value.value().iter().try_for_each(|x| list.write_u24_be(*x))?;
945                    },
946                    _ => {
947                        buffer.write_u8(U32_ARRAY)?;
948                        value.value().iter().try_for_each(|x| list.write_u32(*x))?;
949                    },
950                }
951
952                buffer.write_sized(&list)?;
953            },
954            NodeType::U64Array(value) => {
955                buffer.write_u8(U64_ARRAY)?;
956
957                let mut list = vec![];
958                value.iter().try_for_each(|x| list.write_u64(*x))?;
959                buffer.write_sized(&list)?;
960            },
961            NodeType::F32Array(value) => {
962                buffer.write_u8(F32_ARRAY)?;
963
964                let mut list = vec![];
965                value.iter().try_for_each(|x| list.write_f32(*x))?;
966                buffer.write_sized(&list)?;
967            },
968            NodeType::F64Array(value) => {
969                buffer.write_u8(F64_ARRAY)?;
970
971                let mut list = vec![];
972                value.iter().try_for_each(|x| list.write_f64(*x))?;
973                buffer.write_sized(&list)?;
974            },
975
976            //------------------------------------------------//
977            // Array of complex/specialized nodes.
978            //------------------------------------------------//
979            NodeType::Coord2dArray(value) => {
980                buffer.write_u8(COORD_2D_ARRAY)?;
981
982                let mut list = vec![];
983                value.iter().try_for_each(|x| {
984                    let v1 = list.write_f32(x.x);
985                    let v2 = list.write_f32(x.y);
986                    if v1.is_err() { v1 } else if v2.is_err() { v2 } else { v1 }
987                })?;
988
989                buffer.write_sized(&list)?;
990            },
991            NodeType::Coord3dArray(value) => {
992                buffer.write_u8(COORD_3D_ARRAY)?;
993
994                let mut list = vec![];
995                value.iter().try_for_each(|x| {
996                    let v1 = list.write_f32(x.x);
997                    let v2 = list.write_f32(x.y);
998                    let v3 = list.write_f32(x.z);
999                    if v1.is_err() { v1 } else if v2.is_err() { v2 } else if v3.is_err() { v3 } else { v1 }
1000                })?;
1001
1002                buffer.write_sized(&list)?;
1003            },
1004
1005            //------------------------------------------------//
1006            // Array of string nodes.
1007            //------------------------------------------------//
1008            NodeType::Utf16Array(value) => {
1009                buffer.write_u8(UTF16_ARRAY)?;
1010
1011                let mut list = vec![];
1012                value.iter().try_for_each(|y| {
1013                    list.write_u32(strings_utf16.iter().position(|x| x == y).unwrap() as u32)
1014                })?;
1015
1016                buffer.write_sized(&list)?;
1017            },
1018            NodeType::AsciiArray(value) => {
1019                buffer.write_u8(ASCII_ARRAY)?;
1020
1021                let mut list = vec![];
1022                value.iter().try_for_each(|y| {
1023                    list.write_u32(strings_utf8.iter().position(|x| x == y).unwrap() as u32)
1024                })?;
1025
1026                buffer.write_sized(&list)?;
1027            },
1028
1029            //------------------------------------------------//
1030            // Array of unknown nodes.
1031            //------------------------------------------------//
1032            NodeType::AngleArray(value) => {
1033                buffer.write_u8(ANGLE_ARRAY)?;
1034
1035                let mut list = vec![];
1036                value.iter().try_for_each(|x| list.write_i16(*x))?;
1037                buffer.write_sized(&list)?;
1038            },
1039
1040            //------------------------------------------------//
1041            // Record nodes.
1042            //------------------------------------------------//
1043            NodeType::Record(value) => {
1044                if value.record_flags().contains(RecordNodeFlags::HAS_NON_OPTIMIZED_INFO) || is_root_node {
1045                    buffer.write_u8(value.record_flags().bits())?;
1046
1047                    buffer.write_u16(record_names.iter().position(|x| x == &value.name).unwrap() as u16)?;
1048                    buffer.write_u8(value.version)?;
1049                }
1050
1051                // If it's not the root node or uses optimized data, it needs special encoding.
1052                // The games write its two bytes one by one, which matters for where their write buffer flushes.
1053                else {
1054                    let mut info: u16 = (value.record_flags().bits() as u16) << 8;
1055                    info |= (value.version as u16) << 9;
1056                    info |= record_names.iter().position(|x| x == &value.name).unwrap() as u16;
1057
1058                    let [high, low] = info.to_be_bytes();
1059                    buffer.write_u8(high)?;
1060                    buffer.write_u8(low)?;
1061                }
1062
1063                let block = buffer.open_size()?;
1064                if value.record_flags.contains(RecordNodeFlags::HAS_NESTED_BLOCKS) {
1065                    let groups = buffer.open_size()?;
1066                    for group_node in &value.children {
1067                        let entry = buffer.open_size()?;
1068                        for node in group_node {
1069                            Self::save_node(buffer, node, false, record_names, strings_utf8, strings_utf16)?;
1070                        }
1071                        let entry_size = buffer.len() - buffer.end_of(&entry);
1072                        buffer.close_size(entry, entry_size as u32)?;
1073                    }
1074
1075                    // The block size of nested records counts from after their group count.
1076                    let groups_end = buffer.close_size(groups, value.children.len() as u32)?;
1077                    let block_size = buffer.len() - groups_end;
1078                    buffer.close_size(block, block_size as u32)?;
1079                } else {
1080
1081                    // For non-nested nodes, we just get the first and only children group.
1082                    if let Some(children) = value.children.first() {
1083                        for node in children {
1084                            Self::save_node(buffer, node, false, record_names, strings_utf8, strings_utf16)?;
1085                        }
1086                    }
1087
1088                    let block_size = buffer.len() - buffer.end_of(&block);
1089                    buffer.close_size(block, block_size as u32)?;
1090                }
1091            },
1092        }
1093
1094        Ok(())
1095    }
1096
1097    //---------------------------------------------------------------------------//
1098    //                       Utility functions for encoding
1099    //---------------------------------------------------------------------------//
1100
1101    /// Recursively collects all strings from a node tree for string table generation.
1102    ///
1103    /// This function traverses the entire node tree starting from the given node,
1104    /// collecting unique strings into separate vectors for use during encoding.
1105    /// Strings are deduplicated to avoid redundant entries in the string tables.
1106    ///
1107    /// # Arguments
1108    ///
1109    /// * `node_type` - The node to collect strings from (typically the root node)
1110    /// * `record_names` - Output vector for record node names
1111    /// * `strings_utf8` - Output vector for ASCII/UTF-8 strings
1112    /// * `strings_utf16` - Output vector for UTF-16 strings
1113    ///
1114    /// # String Categories
1115    ///
1116    /// - **Record names**: Names of record nodes (e.g., "FACTION", "CAMPAIGN_SAVE_GAME")
1117    /// - **UTF-8 strings**: Values from `Ascii` and `AsciiArray` nodes
1118    /// - **UTF-16 strings**: Values from `Utf16` and `Utf16Array` nodes
1119    ///
1120    /// # Usage
1121    ///
1122    /// Called during encoding to build string tables before node serialization.
1123    /// The resulting vectors are used to look up string indices when encoding
1124    /// string nodes.
1125    pub(crate) fn read_string_from_node(node_type: &NodeType, record_names: &mut Vec<String>, strings_utf8: &mut Vec<String>, strings_utf16: &mut Vec<String>) {
1126        match node_type {
1127            NodeType::Utf16(value) if !strings_utf16.contains(value) => strings_utf16.push(value.to_owned()),
1128            NodeType::Ascii(value) if !strings_utf8.contains(value) => strings_utf8.push(value.to_owned()),
1129            NodeType::Utf16Array(value) => value.iter().for_each(|value| if !strings_utf16.contains(value) { strings_utf16.push(value.to_owned()) }),
1130            NodeType::AsciiArray(value) => value.iter().for_each(|value| if !strings_utf8.contains(value) { strings_utf8.push(value.to_owned()) }),
1131            NodeType::Record(value) => {
1132                if !record_names.contains(&value.name) {
1133                    record_names.push(value.name.to_owned());
1134                }
1135                for node_group in &value.children {
1136                    for node in node_group {
1137                        Self::read_string_from_node(node, record_names, strings_utf8, strings_utf16);
1138                    }
1139                }
1140            },
1141
1142            // Skip any other node.
1143            _ => {}
1144        }
1145    }
1146}
1147
1148//---------------------------------------------------------------------------//
1149//                       String table order
1150//---------------------------------------------------------------------------//
1151
1152/// Seed of the games' string hash.
1153const STRING_HASH_SEED: u32 = 0xf73a_51c9;
1154
1155/// Buckets of the games' hash maps before they first grow.
1156const HASH_MAP_INITIAL_BUCKETS: usize = 4;
1157
1158/// Returns the hash the games give an ASCII string table entry: every byte (sign-extended) xored into the seed, from
1159/// the last one, shifted by 0, 8, 16 and 24 bits in turn.
1160pub(crate) fn string_hash_u8(string: &str) -> u32 {
1161    string_hash(string.bytes().map(|byte| byte as i8 as u32))
1162}
1163
1164/// Returns the hash the games give a UTF-16 string table entry, like [`string_hash_u8`] over its UTF-16 units.
1165pub(crate) fn string_hash_u16(string: &str) -> u32 {
1166    string_hash(string.encode_utf16().map(u32::from).collect::<Vec<_>>().into_iter())
1167}
1168
1169/// Hashes the units of a string, from the last one.
1170fn string_hash(units: impl DoubleEndedIterator<Item = u32>) -> u32 {
1171    units.rev().zip((0..4).cycle()).fold(STRING_HASH_SEED, |hash, (unit, step)| hash ^ unit.wrapping_shl(step * 8))
1172}
1173
1174/// Returns the order the games list a hash map in (string tables, AI object maps): the map keeps entries at the end of
1175/// their bucket and grows to `2n + 1` buckets (moving entries bucket by bucket) when it holds more entries than buckets.
1176///
1177/// # Arguments
1178///
1179/// * `hashes` - Hash of each entry, in the order they were added.
1180///
1181/// # Returns
1182///
1183/// The indexes of the entries, in map order.
1184pub fn hash_map_order(hashes: &[u32]) -> Vec<usize> {
1185    let mut buckets = vec![vec![]; HASH_MAP_INITIAL_BUCKETS];
1186    for (index, hash) in hashes.iter().enumerate() {
1187        if index + 1 > buckets.len() {
1188            let mut grown = vec![vec![]; buckets.len() * 2 + 1];
1189            let count = grown.len();
1190            for entry in buckets.into_iter().flatten() {
1191                grown[hashes[entry] as usize % count].push(entry);
1192            }
1193            buckets = grown;
1194        }
1195        let count = buckets.len();
1196        buckets[*hash as usize % count].push(index);
1197    }
1198    buckets.into_iter().flatten().collect()
1199}