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}