Skip to main content

rpfm_lib/files/table/
mod.rs

1//---------------------------------------------------------------------------//
2// Copyright (c) 2017-2026 Ismael Gutiérrez González. All rights reserved.
3//
4// This file is part of the Rusted PackFile Manager (RPFM) project,
5// which can be found here: https://github.com/Frodo45127/rpfm.
6//
7// This file is licensed under the MIT license, which can be found here:
8// https://github.com/Frodo45127/rpfm/blob/master/LICENSE.
9//---------------------------------------------------------------------------//
10
11//! Table data structures and encoding/decoding for Total War game data.
12//!
13//! # Overview
14//!
15//! This module provides the core abstraction for working with structured table data in Total War games.
16//! Tables are the primary format for storing game data like units, buildings, technologies, factions,
17//! and thousands of other data types. They function similarly to database tables with typed columns
18//! and rows of data.
19//!
20//! # Table Data Flow
21//!
22//! ```text
23//! ┌──────────────┐
24//! │  DB File     │  Binary format on disk
25//! │  (Header +   │  Contains: version, GUID, entry count
26//! │   Table Data)│
27//! └──────┬───────┘
28//!        │ decode (requires Definition from schema)
29//!        ↓
30//! ┌──────────────┐
31//! │ TableInMemory│  In-memory representation
32//! │ (Rows of     │  Vec<Vec<DecodedData>>
33//! │  DecodedData)│
34//! └──────┬───────┘
35//!        │ export/import
36//!        ↓
37//! ┌──────────────┐
38//! │ TSV/SQL/etc  │  Human-readable formats
39//! └──────────────┘
40//! ```
41//!
42//! # Key Components
43//!
44//! ## Table Trait
45//!
46//! The [`Table`] trait defines the interface for all table-like structures. It provides:
47//! - Access to table metadata (name, definition, patches)
48//! - Row data access (immutable and mutable)
49//! - Column queries and row generation
50//! - Data validation and replacement
51//!
52//! ## DecodedData Enum
53//!
54//! The [`DecodedData`] enum represents all possible data types in Total War tables:
55//! - **Primitives**: Boolean, integers (I16/I32/I64), floats (F32/F64)
56//! - **Strings**: UTF-8 (StringU8) and UTF-16 (StringU16)
57//! - **Optional types**: OptionalI16/I32/I64, OptionalStringU8/U16
58//! - **Special types**: ColourRGB (merged R/G/B values), SequenceU16/U32 (binary blobs)
59//!
60//! ## TableInMemory
61//!
62//! The concrete implementation of the `Table` trait for in-memory table manipulation.
63//! Supports encoding/decoding to/from binary format, TSV import/export, and SQL operations.
64//!
65//! # Encoding/Decoding
66//!
67//! Tables are encoded in a binary format defined by a [`Definition`] from the schema system:
68//!
69//! 1. **Decoding Process**:
70//!    - Read table header (version, GUID, entry count) from DB file
71//!    - Use Definition to determine column structure and types
72//!    - Decode each row according to field types
73//!    - Apply postprocessing (bitwise fields, enums, color merging)
74//!
75//! 2. **Encoding Process**:
76//!    - Apply preprocessing (split colors, encode enums, combine bitwise fields)
77//!    - Write each field according to its type
78//!    - Update DB file header with entry count
79//!
80//! # Special Field Types
81//!
82//! ## Bitwise Fields
83//!
84//! Integer fields marked as "bitwise" in the definition are split into multiple boolean columns:
85//!
86//! ```text
87//! Binary: single i32 with flags 0b00001011
88//! Decoded: multiple boolean columns [true, true, false, true]
89//! ```
90//!
91//! ## Enum Fields
92//!
93//! Integer fields with enum values defined in the schema show string values:
94//!
95//! ```text
96//! Binary: i32 value 2
97//! Decoded: StringU8("cavalry") based on enum mapping
98//! ```
99//!
100//! ## Split Color Fields
101//!
102//! RGB color components are merged into a single ColourRGB field:
103//!
104//! ```text
105//! Binary: three i32 fields (red=255, green=128, blue=0)
106//! Decoded: single ColourRGB("#FF8000")
107//! ```
108//!
109//! # Usage Example
110//!
111//! ```ignore
112//! use rpfm_lib::files::table::{Table, DecodedData};
113//! use rpfm_lib::files::table::local::TableInMemory;
114//! use rpfm_lib::schema::Definition;
115//!
116//! // Decode a table
117//! let mut table = TableInMemory::decode(&mut reader, &definition, &name, &patches)?;
118//!
119//! // Access rows
120//! for row in table.data().iter() {
121//!     if let DecodedData::StringU8(name) = &row[0] {
122//!         println!("Row name: {}", name);
123//!     }
124//! }
125//!
126//! // Modify data
127//! let mut new_row = table.new_row();
128//! new_row[0] = DecodedData::StringU8("new_unit".to_string());
129//! table.data_mut().push(new_row);
130//!
131//! // Encode back to binary
132//! table.encode(&mut writer)?;
133//! ```
134//!
135//! # Integration
136//!
137//! This module integrates with:
138//! - **Schema module**: Provides [`Definition`] for table structure
139//! - **DB module**: Container format for table data with headers
140//! - **Binary module**: Low-level I/O operations
141//! - **Assembly Kit**: Import/export of official modding tools data
142//!
143//! # See Also
144//!
145//! - [`local::TableInMemory`] - Main implementation
146//! - [`DecodedData`] - Data type enum
147//! - [`crate::files::db`] - DB file container format
148//! - [`crate::schema::Definition`] - Table schema definitions
149
150use base64::{Engine, engine::general_purpose::STANDARD};
151use float_eq::float_eq;
152use serde_derive::{Serialize, Deserialize};
153
154use std::borrow::Cow;
155use std::collections::{BTreeMap, HashMap};
156use std::hash::{Hash, Hasher};
157use std::io::SeekFrom;
158
159use crate::error::{RLibError, Result};
160use crate::binary::{ReadBytes, WriteBytes};
161use crate::schema::*;
162use crate::utils::parse_str_as_bool;
163
164pub mod local;
165
166//---------------------------------------------------------------------------//
167//                              Enum & Structs
168//---------------------------------------------------------------------------//
169
170/// Abstract interface for table-like data structures.
171///
172/// This trait defines the common interface for all table implementations in RPFM.
173/// Tables are row-based data structures with typed columns defined by a [`Definition`].
174///
175/// # Thread Safety
176///
177/// Implementations must be `Send + Sync` to support concurrent access and modification
178/// in multi-threaded environments.
179///
180/// # Implementations
181///
182/// - [`local::TableInMemory`] - In-memory table with full encode/decode support
183///
184/// # Usage
185///
186/// ```ignore
187/// fn process_table<T: Table>(table: &T) {
188///     // Access metadata
189///     let name = table.name();
190///     let definition = table.definition();
191///
192///     // Iterate rows
193///     for row in table.data().iter() {
194///         // Process each row
195///     }
196/// }
197/// ```
198pub trait Table: Send + Sync {
199
200    /// Returns the table name (e.g., "units_tables", "factions_tables").
201    fn name(&self) -> &str;
202
203    /// Returns the table's schema definition.
204    ///
205    /// The definition specifies column structure, types, constraints, and version information.
206    fn definition(&self) -> &Definition;
207
208    /// Returns definition patches applied to this table.
209    ///
210    /// Patches allow runtime modification of definitions for specific tables without
211    /// changing the base schema.
212    fn patches(&self) -> &DefinitionPatch;
213
214    /// Returns the table's row data.
215    ///
216    /// Returns a `Cow` to allow zero-copy access for immutable operations while
217    /// supporting owned data when needed.
218    fn data(&'_ self) -> Cow<'_, [Vec<DecodedData>]>;
219
220    /// Returns a mutable reference to the table's row data.
221    ///
222    /// # Safety
223    ///
224    /// Using this method makes you responsible for maintaining data validity:
225    /// - Each row must have the correct number of columns
226    /// - Each cell must match the type specified in the definition
227    /// - Data integrity is not automatically validated
228    fn data_mut(&mut self) -> &mut Vec<Vec<DecodedData>>;
229
230    /// Sets the table name.
231    fn set_name(&mut self, val: String);
232
233    /// Replaces the table's definition and migrates data to match the new schema.
234    ///
235    /// This method enables **version migration**: converting table data from one schema
236    /// version to another. When the definition changes:
237    /// - New columns are added with default values
238    /// - Removed columns are dropped
239    /// - Type changes are converted where possible
240    /// - Data is validated against the new schema
241    ///
242    /// # Use Cases
243    ///
244    /// - Updating tables after game patches change the schema
245    /// - Converting tables between game versions
246    /// - Applying definition modifications from patches
247    fn set_definition(&mut self, new_definition: &Definition);
248
249    /// Replaces the table's data with the provided rows.
250    ///
251    /// # Validation
252    ///
253    /// This method validates that:
254    /// - Each row has the correct number of columns
255    /// - Each cell matches the expected type from the definition
256    ///
257    /// # Errors
258    ///
259    /// Returns an error if validation fails.
260    fn set_data(&mut self, data: &[Vec<DecodedData>]) -> Result<()>;
261
262    /// Returns the column index for a given column name.
263    ///
264    /// Returns `None` if no column with the specified name exists.
265    /// Column names are case-sensitive.
266    fn column_position_by_name(&self, column_name: &str) -> Option<usize>;
267
268    /// Returns `true` if the table contains no rows.
269    fn is_empty(&self) -> bool;
270
271    /// Returns the number of rows in the table.
272    fn len(&self) -> usize;
273
274    /// Creates a new empty row with default values for all columns.
275    ///
276    /// Default values are determined by:
277    /// 1. Field-specific default values from the definition/patches
278    /// 2. Type-specific defaults (0 for numbers, empty string for text, false for booleans)
279    fn new_row(&self) -> Vec<DecodedData> {
280        let definition = self.definition();
281        let schema_patches = Some(self.patches());
282
283        definition.fields_processed().iter()
284            .map(|field|
285                match field.field_type() {
286                    FieldType::Boolean => {
287                        if let Some(default_value) = field.default_value(schema_patches) {
288                            if default_value.to_lowercase() == "true" {
289                                DecodedData::Boolean(true)
290                            } else {
291                                DecodedData::Boolean(false)
292                            }
293                        } else {
294                            DecodedData::Boolean(false)
295                        }
296                    }
297                    FieldType::F32 => {
298                        if let Some(default_value) = field.default_value(schema_patches) {
299                            if let Ok(default_value) = default_value.parse::<f32>() {
300                                DecodedData::F32(default_value)
301                            } else {
302                                DecodedData::F32(0.0)
303                            }
304                        } else {
305                            DecodedData::F32(0.0)
306                        }
307                    },
308                    FieldType::F64 => {
309                        if let Some(default_value) = field.default_value(schema_patches) {
310                            if let Ok(default_value) = default_value.parse::<f64>() {
311                                DecodedData::F64(default_value)
312                            } else {
313                                DecodedData::F64(0.0)
314                            }
315                        } else {
316                            DecodedData::F64(0.0)
317                        }
318                    },
319                    FieldType::I16 => {
320                        if let Some(default_value) = field.default_value(schema_patches) {
321                            if let Ok(default_value) = default_value.parse::<i16>() {
322                                DecodedData::I16(default_value)
323                            } else {
324                                DecodedData::I16(0)
325                            }
326                        } else {
327                            DecodedData::I16(0)
328                        }
329                    },
330                    FieldType::I32 => {
331                        if let Some(default_value) = field.default_value(schema_patches) {
332                            if let Ok(default_value) = default_value.parse::<i32>() {
333                                DecodedData::I32(default_value)
334                            } else {
335                                DecodedData::I32(0)
336                            }
337                        } else {
338                            DecodedData::I32(0)
339                        }
340                    },
341                    FieldType::I64 => {
342                        if let Some(default_value) = field.default_value(schema_patches) {
343                            if let Ok(default_value) = default_value.parse::<i64>() {
344                                DecodedData::I64(default_value)
345                            } else {
346                                DecodedData::I64(0)
347                            }
348                        } else {
349                            DecodedData::I64(0)
350                        }
351                    },
352
353                    FieldType::ColourRGB => {
354                        if let Some(default_value) = field.default_value(schema_patches) {
355                            if u32::from_str_radix(&default_value, 16).is_ok() {
356                                DecodedData::ColourRGB(default_value)
357                            } else {
358                                DecodedData::ColourRGB("000000".to_owned())
359                            }
360                        } else {
361                            DecodedData::ColourRGB("000000".to_owned())
362                        }
363                    },
364                    FieldType::StringU8 => {
365                        if let Some(default_value) = field.default_value(schema_patches) {
366                            DecodedData::StringU8(default_value)
367                        } else {
368                            DecodedData::StringU8(String::new())
369                        }
370                    }
371                    FieldType::StringU16 => {
372                        if let Some(default_value) = field.default_value(schema_patches) {
373                            DecodedData::StringU16(default_value)
374                        } else {
375                            DecodedData::StringU16(String::new())
376                        }
377                    }
378
379                    FieldType::OptionalI16 => {
380                        if let Some(default_value) = field.default_value(schema_patches) {
381                            if let Ok(default_value) = default_value.parse::<i16>() {
382                                DecodedData::OptionalI16(default_value)
383                            } else {
384                                DecodedData::OptionalI16(0)
385                            }
386                        } else {
387                            DecodedData::OptionalI16(0)
388                        }
389                    },
390                    FieldType::OptionalI32 => {
391                        if let Some(default_value) = field.default_value(schema_patches) {
392                            if let Ok(default_value) = default_value.parse::<i32>() {
393                                DecodedData::OptionalI32(default_value)
394                            } else {
395                                DecodedData::OptionalI32(0)
396                            }
397                        } else {
398                            DecodedData::OptionalI32(0)
399                        }
400                    },
401                    FieldType::OptionalI64 => {
402                        if let Some(default_value) = field.default_value(schema_patches) {
403                            if let Ok(default_value) = default_value.parse::<i64>() {
404                                DecodedData::OptionalI64(default_value)
405                            } else {
406                                DecodedData::OptionalI64(0)
407                            }
408                        } else {
409                            DecodedData::OptionalI64(0)
410                        }
411                    },
412
413                    FieldType::OptionalStringU8 => {
414                        if let Some(default_value) = field.default_value(schema_patches) {
415                            DecodedData::OptionalStringU8(default_value)
416                        } else {
417                            DecodedData::OptionalStringU8(String::new())
418                        }
419                    }
420                    FieldType::OptionalStringU16 => {
421                        if let Some(default_value) = field.default_value(schema_patches) {
422                            DecodedData::OptionalStringU16(default_value)
423                        } else {
424                            DecodedData::OptionalStringU16(String::new())
425                        }
426                    },
427                    FieldType::SequenceU16(_) => DecodedData::SequenceU16(vec![0, 0]),
428                    FieldType::SequenceU32(_) => DecodedData::SequenceU32(vec![0, 0, 0, 0])
429                }
430            )
431            .collect()
432    }
433
434    /// This function tries to find all rows with the provided data, if they exists in this table.
435    fn rows_containing_data(&self, column_name: &str, data: &str) -> Option<(usize, Vec<usize>)>;
436}
437
438/// Runtime representation of table cell data supporting 16 different data types.
439///
440/// This enum provides a type-safe wrapper around the various data types that can appear
441/// in Total War game data tables. Each variant corresponds to a [`FieldType`] from the
442/// schema definition.
443///
444/// # Data Type Categories
445///
446/// ## Numeric Types
447/// - **Boolean**: True/false values
448/// - **F32/F64**: Floating-point numbers (32-bit and 64-bit precision)
449/// - **I16/I32/I64**: Signed integers (16-bit, 32-bit, 64-bit)
450///
451/// ## String Types
452/// - **StringU8/StringU16**: UTF-8 strings with length prefix (u8 or u16)
453/// - **ColourRGB**: Hexadecimal color strings (e.g., "FF0000" for red)
454///
455/// ## Optional Types
456/// - **OptionalI16/I32/I64**: Integer types that can represent "null" via special values
457/// - **OptionalStringU8/U16**: String types that can be empty to represent "null"
458///
459/// ## Sequence Types
460/// - **SequenceU16/U32**: Binary-encoded nested table data
461///
462/// Sequences represent **recursive table structures** - tables embedded within a single
463/// cell. The raw bytes encode a complete nested table with its own rows and columns.
464/// Used in complex tables like unit models where each row can contain sub-tables of
465/// equipment variants or LOD levels.
466///
467/// # Type Conversion
468///
469/// The enum provides automatic conversion between compatible types via
470/// [`convert_between_types`](DecodedData::convert_between_types):
471///
472/// - Numeric types convert with casting (may lose precision)
473/// - Booleans convert to 0/1 for numbers, "true"/"false" for strings
474/// - Strings parse to numbers (returns error if invalid)
475/// - Sequences only convert between U16 and U32 variants
476///
477/// # String Representation
478///
479/// All variants can be converted to strings via
480/// [`data_to_string`](DecodedData::data_to_string):
481///
482/// - Numbers format with appropriate precision (floats: 4 decimals)
483/// - Sequences encode as base64 for display/export
484///
485/// # Usage Example
486///
487/// ```rust
488/// # use rpfm_lib::schema::FieldType;
489/// # use rpfm_lib::files::table::DecodedData;
490/// // Create from type and default value
491/// let health = DecodedData::new_from_type_and_value(
492///     &FieldType::I32,
493///     &Some("100".to_string())
494/// );
495///
496/// // Parse from user input
497/// let name = DecodedData::new_from_type_and_string(
498///     &FieldType::StringU8,
499///     "Empire Swordsmen"
500/// ).unwrap();
501///
502/// // Convert between types
503/// let health_float = health.convert_between_types(&FieldType::F32).unwrap();
504///
505/// // Display value
506/// println!("Health: {}", health.data_to_string());
507/// ```
508#[derive(Clone, Debug, Serialize, Deserialize)]
509pub enum DecodedData {
510    /// Boolean true/false value.
511    Boolean(bool),
512
513    /// 32-bit floating-point number.
514    F32(f32),
515
516    /// 64-bit floating-point number.
517    F64(f64),
518
519    /// 16-bit signed integer.
520    I16(i16),
521
522    /// 32-bit signed integer.
523    I32(i32),
524
525    /// 64-bit signed integer.
526    I64(i64),
527
528    /// RGB color as hex string (e.g., "FF0000" for red).
529    ColourRGB(String),
530
531    /// UTF-8 string with u16 length prefix.
532    StringU8(String),
533
534    /// UTF-16 string with u16 length prefix.
535    StringU16(String),
536
537    /// Optional 16-bit signed integer.
538    OptionalI16(i16),
539
540    /// Optional 32-bit signed integer.
541    OptionalI32(i32),
542
543    /// Optional 64-bit signed integer.
544    OptionalI64(i64),
545
546    /// Optional UTF-8 string with u16 length prefix.
547    OptionalStringU8(String),
548
549    /// Optional UTF-16 string with u16 length prefix.
550    OptionalStringU16(String),
551
552    /// Binary-encoded nested table with u16 entry count.
553    SequenceU16(Vec<u8>),
554
555    /// Binary-encoded nested table with u32 entry count.
556    SequenceU32(Vec<u8>)
557}
558
559//----------------------------------------------------------------//
560// Implementations for `DecodedData`.
561//----------------------------------------------------------------//
562
563/// Eq and PartialEq implementation of `DecodedData`. We need this implementation due to
564/// the float comparison being... special.
565impl Eq for DecodedData {}
566impl PartialEq for DecodedData {
567    fn eq(&self, other: &Self) -> bool {
568        match (self, other) {
569            (DecodedData::Boolean(x), DecodedData::Boolean(y)) => x == y,
570            (DecodedData::F32(x), DecodedData::F32(y)) => float_eq!(x, y, abs <= 0.0001),
571            (DecodedData::F64(x), DecodedData::F64(y)) => float_eq!(x, y, abs <= 0.0001),
572            (DecodedData::I16(x), DecodedData::I16(y)) => x == y,
573            (DecodedData::I32(x), DecodedData::I32(y)) => x == y,
574            (DecodedData::I64(x), DecodedData::I64(y)) => x == y,
575            (DecodedData::ColourRGB(x), DecodedData::ColourRGB(y)) => x == y,
576            (DecodedData::StringU8(x), DecodedData::StringU8(y)) => x == y,
577            (DecodedData::StringU16(x), DecodedData::StringU16(y)) => x == y,
578            (DecodedData::OptionalI16(x), DecodedData::OptionalI16(y)) => x == y,
579            (DecodedData::OptionalI32(x), DecodedData::OptionalI32(y)) => x == y,
580            (DecodedData::OptionalI64(x), DecodedData::OptionalI64(y)) => x == y,
581            (DecodedData::OptionalStringU8(x), DecodedData::OptionalStringU8(y)) => x == y,
582            (DecodedData::OptionalStringU16(x), DecodedData::OptionalStringU16(y)) => x == y,
583            (DecodedData::SequenceU16(x), DecodedData::SequenceU16(y)) => x == y,
584            (DecodedData::SequenceU32(x), DecodedData::SequenceU32(y)) => x == y,
585            _ => false
586        }
587    }
588}
589
590impl Hash for DecodedData {
591    fn hash<H: Hasher>(&self, state: &mut H) {
592        match self {
593            DecodedData::Boolean(y) => y.hash(state),
594            DecodedData::F32(y) => y.to_string().hash(state),
595            DecodedData::F64(y) => y.to_string().hash(state),
596            DecodedData::I16(y) => y.hash(state),
597            DecodedData::I32(y) => y.hash(state),
598            DecodedData::I64(y) => y.hash(state),
599            DecodedData::ColourRGB(y) => y.hash(state),
600            DecodedData::StringU8(y) => y.hash(state),
601            DecodedData::StringU16(y) => y.hash(state),
602            DecodedData::OptionalI16(y) => y.hash(state),
603            DecodedData::OptionalI32(y) => y.hash(state),
604            DecodedData::OptionalI64(y) => y.hash(state),
605            DecodedData::OptionalStringU8(y) => y.hash(state),
606            DecodedData::OptionalStringU16(y) => y.hash(state),
607            DecodedData::SequenceU16(y) => y.hash(state),
608            DecodedData::SequenceU32(y) => y.hash(state),
609        }
610    }
611}
612
613/// Implementation of `DecodedData`.
614impl DecodedData {
615
616    /// Creates a new `DecodedData` of the specified type with an optional default value.
617    ///
618    /// This function is the primary constructor for creating table cell data. It handles
619    /// type-specific initialization and default value parsing.
620    ///
621    /// # Behavior
622    ///
623    /// - If `default_value` is `Some`, attempts to parse it according to `field_type`
624    /// - If parsing fails or `default_value` is `None`, uses type-specific defaults:
625    ///   - Numeric types: 0 or 0.0
626    ///   - Booleans: false
627    ///   - Strings: empty string
628    ///   - Colors: "000000" (black) or "" depending on default_value presence
629    ///   - Sequences: minimal valid data ([0, 0] or [0, 0, 0, 0])
630    ///
631    /// # Examples
632    ///
633    /// ```rust
634    /// # use rpfm_lib::schema::FieldType;
635    /// # use rpfm_lib::files::table::DecodedData;
636    /// // With default value
637    /// let health = DecodedData::new_from_type_and_value(
638    ///     &FieldType::I32,
639    ///     &Some("100".to_string())
640    /// );
641    ///
642    /// // Without default (uses type default)
643    /// let name = DecodedData::new_from_type_and_value(
644    ///     &FieldType::StringU8,
645    ///     &None
646    /// );
647    /// ```
648    pub fn new_from_type_and_value(field_type: &FieldType, default_value: &Option<String>) -> Self {
649        match default_value {
650            Some(default_value) => match field_type {
651                FieldType::Boolean => if let Ok(value) = parse_str_as_bool(default_value) { DecodedData::Boolean(value) } else { DecodedData::Boolean(false) },
652                FieldType::F32 => if let Ok(value) = default_value.parse::<f32>() { DecodedData::F32(value) } else { DecodedData::F32(0.0) },
653                FieldType::F64 => if let Ok(value) = default_value.parse::<f64>() { DecodedData::F64(value) } else { DecodedData::F64(0.0) },
654                FieldType::I16 => if let Ok(value) = default_value.parse::<i16>() { DecodedData::I16(value) } else { DecodedData::I16(0) },
655                FieldType::I32 => if let Ok(value) = default_value.parse::<i32>() { DecodedData::I32(value) } else { DecodedData::I32(0) },
656                FieldType::I64 => if let Ok(value) = default_value.parse::<i64>() { DecodedData::I64(value) } else { DecodedData::I64(0) },
657                FieldType::ColourRGB => if u32::from_str_radix(default_value, 16).is_ok() {
658                    DecodedData::ColourRGB(default_value.to_owned())
659                } else {
660                    DecodedData::ColourRGB("000000".to_owned())
661                },
662
663                FieldType::StringU8 => DecodedData::StringU8(default_value.to_owned()),
664                FieldType::StringU16 => DecodedData::StringU16(default_value.to_owned()),
665                FieldType::OptionalI16 => if let Ok(value) = default_value.parse::<i16>() { DecodedData::I16(value) } else { DecodedData::I16(0) },
666                FieldType::OptionalI32 => if let Ok(value) = default_value.parse::<i32>() { DecodedData::I32(value) } else { DecodedData::I32(0) },
667                FieldType::OptionalI64 => if let Ok(value) = default_value.parse::<i64>() { DecodedData::I64(value) } else { DecodedData::I64(0) },
668                FieldType::OptionalStringU8 => DecodedData::OptionalStringU8(default_value.to_owned()),
669                FieldType::OptionalStringU16 => DecodedData::OptionalStringU16(default_value.to_owned()),
670
671                // For these two ignore the default value.
672                FieldType::SequenceU16(_) => DecodedData::SequenceU16(vec![0, 0]),
673                FieldType::SequenceU32(_) => DecodedData::SequenceU32(vec![0, 0, 0, 0]),
674            }
675            None => match field_type {
676                FieldType::Boolean => DecodedData::Boolean(false),
677                FieldType::F32 => DecodedData::F32(0.0),
678                FieldType::F64 => DecodedData::F64(0.0),
679                FieldType::I16 => DecodedData::I16(0),
680                FieldType::I32 => DecodedData::I32(0),
681                FieldType::I64 => DecodedData::I64(0),
682                FieldType::ColourRGB => DecodedData::ColourRGB("".to_owned()),
683                FieldType::StringU8 => DecodedData::StringU8("".to_owned()),
684                FieldType::StringU16 => DecodedData::StringU16("".to_owned()),
685                FieldType::OptionalI16 => DecodedData::OptionalI16(0),
686                FieldType::OptionalI32 => DecodedData::OptionalI32(0),
687                FieldType::OptionalI64 => DecodedData::OptionalI64(0),
688                FieldType::OptionalStringU8 => DecodedData::OptionalStringU8("".to_owned()),
689                FieldType::OptionalStringU16 => DecodedData::OptionalStringU16("".to_owned()),
690                FieldType::SequenceU16(_) => DecodedData::SequenceU16(vec![0, 0]),
691                FieldType::SequenceU32(_) => DecodedData::SequenceU32(vec![0, 0, 0, 0]),
692            }
693        }
694    }
695
696    /// Parses a string value into a `DecodedData` of the specified type.
697    ///
698    /// This function is used when importing data from text formats (TSV, user input, etc.)
699    /// and requires strict validation - parsing failures return an error.
700    ///
701    /// # Parsing Rules
702    ///
703    /// - **Boolean**: Accepts "true"/"false", "1"/"0", "yes"/"no" (case-insensitive)
704    /// - **Numeric**: Standard Rust parsing (scientific notation supported for floats)
705    /// - **String/Color**: Direct assignment (no validation for colors)
706    /// - **Sequence**: Converts string bytes to `Vec<u8>`
707    ///
708    /// # Errors
709    ///
710    /// Returns an error if:
711    /// - Numeric value cannot be parsed (invalid format or out of range)
712    /// - Boolean value is not recognized
713    ///
714    /// # Examples
715    ///
716    /// ```ignore
717    /// # use rpfm_lib::schema::FieldType;
718    /// # use rpfm_lib::files::table::DecodedData;
719    /// // Parse valid values
720    /// let damage = DecodedData::new_from_type_and_string(&FieldType::I32, "42")?;
721    /// let enabled = DecodedData::new_from_type_and_string(&FieldType::Boolean, "true")?;
722    ///
723    /// // This returns an error - invalid integer
724    /// let invalid = DecodedData::new_from_type_and_string(&FieldType::I32, "not a number");
725    /// assert!(invalid.is_err());
726    /// # Ok::<(), anyhow::Error>(())
727    /// ```
728    pub fn new_from_type_and_string(field_type: &FieldType, value: &str) -> Result<Self> {
729        Ok(match field_type {
730            FieldType::Boolean => Self::Boolean(parse_str_as_bool(value)?),
731            FieldType::F32 => Self::F32(value.parse::<f32>()?),
732            FieldType::F64 => Self::F64(value.parse::<f64>()?),
733            FieldType::I16 => Self::I16(value.parse::<i16>()?),
734            FieldType::I32 => Self::I32(value.parse::<i32>()?),
735            FieldType::I64 => Self::I64(value.parse::<i64>()?),
736            FieldType::ColourRGB => Self::ColourRGB(value.to_string()),
737            FieldType::StringU8 => Self::StringU8(value.to_string()),
738            FieldType::StringU16 => Self::StringU16(value.to_string()),
739            FieldType::OptionalI16 => Self::OptionalI16(value.parse::<i16>()?),
740            FieldType::OptionalI32 => Self::OptionalI32(value.parse::<i32>()?),
741            FieldType::OptionalI64 => Self::OptionalI64(value.parse::<i64>()?),
742            FieldType::OptionalStringU8 => Self::OptionalStringU8(value.to_string()),
743            FieldType::OptionalStringU16 => Self::OptionalStringU16(value.to_string()),
744            FieldType::SequenceU16(_) => Self::SequenceU16(value.as_bytes().to_vec()),
745            FieldType::SequenceU32(_) => Self::SequenceU32(value.as_bytes().to_vec()),
746        })
747    }
748
749    /// Validates that this `DecodedData` matches the expected `FieldType`.
750    ///
751    /// This is used during table validation to ensure type safety - verifying that
752    /// runtime data matches the schema definition.
753    ///
754    /// # Returns
755    ///
756    /// - `true` if the variant matches the field type
757    /// - `false` if there's a type mismatch
758    ///
759    /// # Examples
760    ///
761    /// ```rust
762    /// # use rpfm_lib::schema::FieldType;
763    /// # use rpfm_lib::files::table::DecodedData;
764    /// let data = DecodedData::I32(42);
765    ///
766    /// assert!(data.is_field_type_correct(&FieldType::I32));
767    /// assert!(!data.is_field_type_correct(&FieldType::StringU8));
768    /// ```
769    pub fn is_field_type_correct(&self, field_type: &FieldType) -> bool {
770        match self {
771            DecodedData::Boolean(_) => field_type == &FieldType::Boolean,
772            DecodedData::F32(_) => field_type == &FieldType::F32,
773            DecodedData::F64(_) => field_type == &FieldType::F64,
774            DecodedData::I16(_) => field_type == &FieldType::I16,
775            DecodedData::I32(_) => field_type == &FieldType::I32,
776            DecodedData::I64(_) => field_type == &FieldType::I64,
777            DecodedData::ColourRGB(_) => field_type == &FieldType::ColourRGB,
778            DecodedData::StringU8(_) => field_type == &FieldType::StringU8,
779            DecodedData::StringU16(_) => field_type == &FieldType::StringU16,
780            DecodedData::OptionalI16(_) => field_type == &FieldType::OptionalI16,
781            DecodedData::OptionalI32(_) => field_type == &FieldType::OptionalI32,
782            DecodedData::OptionalI64(_) => field_type == &FieldType::OptionalI64,
783            DecodedData::OptionalStringU8(_) => field_type == &FieldType::OptionalStringU8,
784            DecodedData::OptionalStringU16(_) => field_type == &FieldType::OptionalStringU16,
785            DecodedData::SequenceU16(_) => matches!(field_type, FieldType::SequenceU16(_)),
786            DecodedData::SequenceU32(_) => matches!(field_type, FieldType::SequenceU32(_)),
787        }
788    }
789
790    /// Converts this data to a different `FieldType`, performing automatic type coercion.
791    ///
792    /// This function enables schema migration and type changes by converting data between
793    /// compatible types. Conversion rules vary by type - some are lossless, others may
794    /// truncate or fail.
795    ///
796    /// # Conversion Rules
797    ///
798    /// ## Numeric Conversions
799    /// - **Between numeric types**: Cast with potential precision/range loss
800    /// - **To Boolean**: `true` if value >= 1, otherwise `false`
801    /// - **To String**: Format with `to_string()`
802    ///
803    /// ## Boolean Conversions
804    /// - **To numeric**: `true` → 1, `false` → 0
805    /// - **To string**: "true" or "false"
806    /// - **To color**: "FFFFFF" (white) or "000000" (black)
807    ///
808    /// ## String Conversions
809    /// - **To numeric**: Parse string (returns error if invalid)
810    /// - **To Boolean**: Parse "true"/"false"/"1"/"0" (returns error if invalid)
811    /// - **Between string types**: Direct copy
812    ///
813    /// ## Sequence Conversions
814    /// - **U16 ↔ U32**: Adjust padding bytes to match new entry count size
815    /// - **To other types**: Returns default value for target type (data is lost)
816    ///
817    /// # Errors
818    ///
819    /// Returns an error if:
820    /// - String cannot be parsed to target numeric/boolean type
821    /// - Conversion is fundamentally incompatible
822    ///
823    /// # Performance Note
824    ///
825    /// Converting to the same type performs a clone operation. Use `clone()` directly
826    /// if you need to copy data without type checking overhead.
827    ///
828    /// # Examples
829    ///
830    /// ```ignore
831    /// # use rpfm_lib::schema::FieldType;
832    /// # use rpfm_lib::files::table::DecodedData;
833    /// // Numeric conversions
834    /// let int_value = DecodedData::I32(42);
835    /// let float_value = int_value.convert_between_types(&FieldType::F32)?;
836    ///
837    /// // String to number (can fail)
838    /// let text = DecodedData::StringU8("123".to_string());
839    /// let number = text.convert_between_types(&FieldType::I32)?;
840    ///
841    /// // Invalid conversion returns error
842    /// let invalid = DecodedData::StringU8("not a number".to_string());
843    /// assert!(invalid.convert_between_types(&FieldType::I32).is_err());
844    /// # Ok::<(), anyhow::Error>(())
845    /// ```
846    pub fn convert_between_types(&self, new_field_type: &FieldType) -> Result<Self> {
847        Ok(match self {
848            Self::Boolean(ref data) => match new_field_type {
849                FieldType::Boolean => self.clone(),
850                FieldType::F32 => Self::F32(if *data { 1.0 } else { 0.0 }),
851                FieldType::F64 => Self::F64(if *data { 1.0 } else { 0.0 }),
852                FieldType::I16 => Self::I16(i16::from(*data)),
853                FieldType::I32 => Self::I32(i32::from(*data)),
854                FieldType::I64 => Self::I64(i64::from(*data)),
855                FieldType::ColourRGB => Self::ColourRGB(if *data { "FFFFFF" } else { "000000" }.to_owned()),
856                FieldType::StringU8 => Self::StringU8(data.to_string()),
857                FieldType::StringU16 => Self::StringU16(data.to_string()),
858                FieldType::OptionalI16 => Self::OptionalI16(i16::from(*data)),
859                FieldType::OptionalI32 => Self::OptionalI32(i32::from(*data)),
860                FieldType::OptionalI64 => Self::OptionalI64(i64::from(*data)),
861                FieldType::OptionalStringU8 => Self::OptionalStringU8(data.to_string()),
862                FieldType::OptionalStringU16 => Self::OptionalStringU16(data.to_string()),
863                FieldType::SequenceU16(_) => Self::SequenceU16(vec![0, 0]),
864                FieldType::SequenceU32(_) => Self::SequenceU32(vec![0, 0, 0, 0]),
865            }
866
867            Self::F32(ref data) => match new_field_type {
868                FieldType::Boolean => Self::Boolean(data >= &1.0),
869                FieldType::F32 => self.clone(),
870                FieldType::F64 => Self::F64(*data as f64),
871                FieldType::I16 => Self::I16(*data as i16),
872                FieldType::I32 => Self::I32(*data as i32),
873                FieldType::I64 => Self::I64(*data as i64),
874                FieldType::ColourRGB => Self::ColourRGB(data.to_string()),
875                FieldType::StringU8 => Self::StringU8(data.to_string()),
876                FieldType::StringU16 => Self::StringU16(data.to_string()),
877                FieldType::OptionalI16 => Self::OptionalI16(*data as i16),
878                FieldType::OptionalI32 => Self::OptionalI32(*data as i32),
879                FieldType::OptionalI64 => Self::OptionalI64(*data as i64),
880                FieldType::OptionalStringU8 => Self::OptionalStringU8(data.to_string()),
881                FieldType::OptionalStringU16 => Self::OptionalStringU16(data.to_string()),
882                FieldType::SequenceU16(_) => Self::SequenceU16(vec![0, 0]),
883                FieldType::SequenceU32(_) => Self::SequenceU32(vec![0, 0, 0, 0]),
884            }
885
886            Self::F64(ref data) => match new_field_type {
887                FieldType::Boolean => Self::Boolean(data >= &1.0),
888                FieldType::F32 => Self::F32(*data as f32),
889                FieldType::F64 => self.clone(),
890                FieldType::I16 => Self::I16(*data as i16),
891                FieldType::I32 => Self::I32(*data as i32),
892                FieldType::I64 => Self::I64(*data as i64),
893                FieldType::ColourRGB => Self::ColourRGB(data.to_string()),
894                FieldType::StringU8 => Self::StringU8(data.to_string()),
895                FieldType::StringU16 => Self::StringU16(data.to_string()),
896                FieldType::OptionalI16 => Self::OptionalI16(*data as i16),
897                FieldType::OptionalI32 => Self::OptionalI32(*data as i32),
898                FieldType::OptionalI64 => Self::OptionalI64(*data as i64),
899                FieldType::OptionalStringU8 => Self::OptionalStringU8(data.to_string()),
900                FieldType::OptionalStringU16 => Self::OptionalStringU16(data.to_string()),
901                FieldType::SequenceU16(_) => Self::SequenceU16(vec![0, 0]),
902                FieldType::SequenceU32(_) => Self::SequenceU32(vec![0, 0, 0, 0]),
903            }
904
905            Self::OptionalI16(ref data) |
906            Self::I16(ref data) => match new_field_type {
907                FieldType::Boolean => Self::Boolean(data >= &1),
908                FieldType::F32 => Self::F32(*data as f32),
909                FieldType::F64 => Self::F64(*data as f64),
910                FieldType::I16 => self.clone(),
911                FieldType::I32 => Self::I32(*data as i32),
912                FieldType::I64 => Self::I64(*data as i64),
913                FieldType::ColourRGB => Self::ColourRGB(data.to_string()),
914                FieldType::StringU8 => Self::StringU8(data.to_string()),
915                FieldType::StringU16 => Self::StringU16(data.to_string()),
916                FieldType::OptionalI16 => Self::OptionalI16(*data),
917                FieldType::OptionalI32 => Self::OptionalI32(*data as i32),
918                FieldType::OptionalI64 => Self::OptionalI64(*data as i64),
919                FieldType::OptionalStringU8 => Self::OptionalStringU8(data.to_string()),
920                FieldType::OptionalStringU16 => Self::OptionalStringU16(data.to_string()),
921                FieldType::SequenceU16(_) => Self::SequenceU16(vec![0, 0]),
922                FieldType::SequenceU32(_) => Self::SequenceU32(vec![0, 0, 0, 0]),
923            }
924
925            Self::OptionalI32(ref data) |
926            Self::I32(ref data) => match new_field_type {
927                FieldType::Boolean => Self::Boolean(data >= &1),
928                FieldType::F32 => Self::F32(*data as f32),
929                FieldType::F64 => Self::F64(*data as f64),
930                FieldType::I16 => Self::I16(*data as i16),
931                FieldType::I32 => self.clone(),
932                FieldType::I64 => Self::I64(*data as i64),
933                FieldType::ColourRGB => Self::ColourRGB(data.to_string()),
934                FieldType::StringU8 => Self::StringU8(data.to_string()),
935                FieldType::StringU16 => Self::StringU16(data.to_string()),
936                FieldType::OptionalI16 => Self::OptionalI16(*data as i16),
937                FieldType::OptionalI32 => Self::OptionalI32(*data),
938                FieldType::OptionalI64 => Self::OptionalI64(*data as i64),
939                FieldType::OptionalStringU8 => Self::OptionalStringU8(data.to_string()),
940                FieldType::OptionalStringU16 => Self::OptionalStringU16(data.to_string()),
941                FieldType::SequenceU16(_) => Self::SequenceU16(vec![0, 0]),
942                FieldType::SequenceU32(_) => Self::SequenceU32(vec![0, 0, 0, 0]),
943            }
944
945            Self::OptionalI64(ref data) |
946            Self::I64(ref data) => match new_field_type {
947                FieldType::Boolean => Self::Boolean(data >= &1),
948                FieldType::F32 => Self::F32(*data as f32),
949                FieldType::F64 => Self::F64(*data as f64),
950                FieldType::I16 => Self::I16(*data as i16),
951                FieldType::I32 => Self::I32(*data as i32),
952                FieldType::I64 => self.clone(),
953                FieldType::ColourRGB => Self::ColourRGB(data.to_string()),
954                FieldType::StringU8 => Self::StringU8(data.to_string()),
955                FieldType::StringU16 => Self::StringU16(data.to_string()),
956                FieldType::OptionalI16 => Self::OptionalI16(*data as i16),
957                FieldType::OptionalI32 => Self::OptionalI32(*data as i32),
958                FieldType::OptionalI64 => Self::OptionalI64(*data),
959                FieldType::OptionalStringU8 => Self::OptionalStringU8(data.to_string()),
960                FieldType::OptionalStringU16 => Self::OptionalStringU16(data.to_string()),
961                FieldType::SequenceU16(_) => Self::SequenceU16(vec![0, 0]),
962                FieldType::SequenceU32(_) => Self::SequenceU32(vec![0, 0, 0, 0]),
963            }
964
965            Self::ColourRGB(ref data) |
966            Self::StringU8(ref data) |
967            Self::StringU16(ref data) |
968            Self::OptionalStringU8(ref data) |
969            Self::OptionalStringU16(ref data) => match new_field_type {
970                FieldType::Boolean => Self::Boolean(parse_str_as_bool(data)?),
971                FieldType::F32 => Self::F32(data.parse::<f32>()?),
972                FieldType::F64 => Self::F64(data.parse::<f64>()?),
973                FieldType::I16 => Self::I16(data.parse::<i16>()?),
974                FieldType::I32 => Self::I32(data.parse::<i32>()?),
975                FieldType::I64 => Self::I64(data.parse::<i64>()?),
976                FieldType::ColourRGB => Self::ColourRGB(data.to_string()),
977                FieldType::StringU8 => Self::StringU8(data.to_string()),
978                FieldType::StringU16 => Self::StringU16(data.to_string()),
979                FieldType::OptionalI16 => Self::OptionalI16(data.parse::<i16>()?),
980                FieldType::OptionalI32 => Self::OptionalI32(data.parse::<i32>()?),
981                FieldType::OptionalI64 => Self::OptionalI64(data.parse::<i64>()?),
982                FieldType::OptionalStringU8 => Self::OptionalStringU8(data.to_string()),
983                FieldType::OptionalStringU16 => Self::OptionalStringU16(data.to_string()),
984                FieldType::SequenceU16(_) => Self::SequenceU16(vec![0, 0]),
985                FieldType::SequenceU32(_) => Self::SequenceU32(vec![0, 0, 0, 0]),
986            }
987
988            Self::SequenceU16(data) => match new_field_type {
989                FieldType::SequenceU16(_) => Self::SequenceU16(data.to_vec()),
990                FieldType::SequenceU32(_) => Self::SequenceU32({
991                    let mut vec = vec![];
992                    vec.extend_from_slice(&data[0..2]);
993                    vec.extend_from_slice(&[0, 0]);
994                    vec.extend_from_slice(&data[2..]);
995                    vec
996                }),
997                _ => Self::new_from_type_and_value(new_field_type, &None),
998            }
999            Self::SequenceU32(data) => match new_field_type {
1000                FieldType::SequenceU16(_) => Self::SequenceU16({
1001                    let mut vec = data[0..2].to_vec();
1002                    vec.extend_from_slice(&data[4..]);
1003                    vec
1004                }),
1005                FieldType::SequenceU32(_) => Self::SequenceU32(data.to_vec()),
1006                _ => Self::new_from_type_and_value(new_field_type, &None),
1007            }
1008        })
1009    }
1010
1011    /// Converts the data to a human-readable string representation.
1012    ///
1013    /// This function formats data for display in UI, export to text formats (TSV),
1014    /// or logging. The formatting is optimized for readability rather than
1015    /// round-trip serialization.
1016    ///
1017    /// # Formatting Rules
1018    ///
1019    /// - **Boolean**: "true" or "false"
1020    /// - **Floats (F32/F64)**: Fixed 4 decimal places (e.g., "3.1416")
1021    /// - **Integers**: Decimal format (e.g., "42")
1022    /// - **Strings/Colors**: Direct output
1023    /// - **Sequences**: Base64-encoded binary data
1024    ///
1025    /// # Performance
1026    ///
1027    /// Returns `Cow<str>` to avoid allocation for string types (zero-copy),
1028    /// while formatting numeric types into owned strings only when needed.
1029    ///
1030    /// # Examples
1031    ///
1032    /// ```rust
1033    /// # use rpfm_lib::files::table::DecodedData;
1034    /// assert_eq!(DecodedData::Boolean(true).data_to_string(), "true");
1035    /// assert_eq!(DecodedData::I32(42).data_to_string(), "42");
1036    /// assert_eq!(DecodedData::F32(3.14159).data_to_string(), "3.1416");
1037    /// ```
1038    pub fn data_to_string(&'_ self) -> Cow<'_, str> {
1039        match self {
1040            DecodedData::Boolean(data) => Cow::from(if *data { "true" } else { "false" }),
1041            DecodedData::F32(data) => Cow::from(format!("{data:.4}")),
1042            DecodedData::F64(data) => Cow::from(format!("{data:.4}")),
1043            DecodedData::I16(data) => Cow::from(data.to_string()),
1044            DecodedData::I32(data) => Cow::from(data.to_string()),
1045            DecodedData::I64(data) => Cow::from(data.to_string()),
1046            DecodedData::OptionalI16(data) => Cow::from(data.to_string()),
1047            DecodedData::OptionalI32(data) => Cow::from(data.to_string()),
1048            DecodedData::OptionalI64(data) => Cow::from(data.to_string()),
1049            DecodedData::ColourRGB(data) |
1050            DecodedData::StringU8(data) |
1051            DecodedData::StringU16(data) |
1052            DecodedData::OptionalStringU8(data) |
1053            DecodedData::OptionalStringU16(data) => Cow::from(data),
1054            DecodedData::SequenceU16(data) |
1055            DecodedData::SequenceU32(data) => Cow::from(STANDARD.encode(data)),
1056        }
1057    }
1058
1059    /// Updates the value while preserving the current type.
1060    ///
1061    /// This method parses the provided string and updates the internal value,
1062    /// maintaining the same `DecodedData` variant. Used for in-place editing
1063    /// in table cells.
1064    ///
1065    /// # Parsing
1066    ///
1067    /// - **Numeric types**: Parse from string (scientific notation supported for floats)
1068    /// - **Boolean**: Parse "true"/"false"/"1"/"0"/"yes"/"no" (case-insensitive)
1069    /// - **String/Color**: Direct assignment (no validation)
1070    /// - **Sequence**: Converts string bytes to `Vec<u8>`
1071    ///
1072    /// # Errors
1073    ///
1074    /// Returns an error if:
1075    /// - Numeric value cannot be parsed (invalid format or out of range)
1076    /// - Boolean value is not recognized
1077    ///
1078    /// # Examples
1079    ///
1080    /// ```ignore
1081    /// # use rpfm_lib::files::table::DecodedData;
1082    /// let mut health = DecodedData::I32(100);
1083    /// health.set_data("150")?;
1084    /// assert_eq!(health.data_to_string(), "150");
1085    ///
1086    /// // Type is preserved - this fails because "abc" isn't a valid integer
1087    /// let result = health.set_data("abc");
1088    /// assert!(result.is_err());
1089    /// # Ok::<(), anyhow::Error>(())
1090    /// ```
1091    pub fn set_data(&mut self, new_data: &str) -> Result<()> {
1092        match self {
1093            Self::Boolean(data) => *data = parse_str_as_bool(new_data)?,
1094            Self::F32(data) => *data = new_data.parse::<f32>()?,
1095            Self::F64(data) => *data = new_data.parse::<f64>()?,
1096            Self::I16(data) => *data = new_data.parse::<i16>()?,
1097            Self::I32(data) => *data = new_data.parse::<i32>()?,
1098            Self::I64(data) => *data = new_data.parse::<i64>()?,
1099            Self::ColourRGB(data) => *data = new_data.to_string(),
1100            Self::StringU8(data) => *data = new_data.to_string(),
1101            Self::StringU16(data) => *data = new_data.to_string(),
1102            Self::OptionalI16(data) => *data = new_data.parse::<i16>()?,
1103            Self::OptionalI32(data) => *data = new_data.parse::<i32>()?,
1104            Self::OptionalI64(data) => *data = new_data.parse::<i64>()?,
1105            Self::OptionalStringU8(data) => *data = new_data.to_string(),
1106            Self::OptionalStringU16(data) => *data = new_data.to_string(),
1107            Self::SequenceU16(data) => *data = new_data.as_bytes().to_vec(),
1108            Self::SequenceU32(data) => *data = new_data.as_bytes().to_vec(),
1109        };
1110
1111        Ok(())
1112    }
1113}
1114
1115//----------------------------------------------------------------//
1116// Util functions for tables.
1117//----------------------------------------------------------------//
1118
1119/// Escapes newlines and tabs for TSV export.
1120///
1121/// Converts literal newline (`\n`) and tab (`\t`) characters to their escaped
1122/// representations (`\\n` and `\\t`) to prevent breaking TSV file structure.
1123///
1124/// # Performance
1125///
1126/// Only allocates/modifies if the string actually contains newlines or tabs,
1127/// using `memchr` for fast scanning. This optimization is critical because
1128/// this function is called for every string cell during table encoding.
1129///
1130/// # In-place Modification
1131///
1132/// The string is modified in-place to avoid unnecessary allocations.
1133fn escape_special_chars(data: &mut String) {
1134
1135    // When performed on mass, this takes 25% of the time to decode a table. Only do it if we really have characters to replace.
1136    if memchr::memchr(b'\n', data.as_bytes()).is_some() || memchr::memchr(b'\t', data.as_bytes()).is_some() {
1137        let mut output = Vec::with_capacity(data.len() + 10);
1138        for c in data.bytes() {
1139            match c {
1140                b'\n' => output.extend_from_slice(b"\\\\n"),
1141                b'\t' => output.extend_from_slice(b"\\\\t"),
1142                _ => output.push(c),
1143            }
1144        }
1145
1146        unsafe { *data.as_mut_vec() = output };
1147    }
1148}
1149
1150/// Unescapes newlines and tabs from TSV import.
1151///
1152/// Converts escaped representations (`\\n` and `\\t`) back to their literal
1153/// characters (`\n` and `\t`). This is the inverse of [`escape_special_chars`].
1154///
1155/// # Returns
1156///
1157/// A new `String` with escape sequences replaced by actual whitespace characters.
1158fn unescape_special_chars(data: &str) -> String {
1159    data.replace("\\\\t", "\t").replace("\\\\n", "\n")
1160}
1161
1162//----------------------------------------------------------------//
1163// Decoding and encoding functions for tables.
1164//----------------------------------------------------------------//
1165
1166/// Decodes a binary table into a vector of rows, each containing decoded field data.
1167///
1168/// This function reads binary data according to the provided table definition and converts
1169/// it into structured `DecodedData` values. It handles both root tables (with externally
1170/// provided entry count) and nested tables (where entry count is read from the data).
1171///
1172/// # Arguments
1173///
1174/// * `data` - Binary data source implementing `ReadBytes`.
1175/// * `definition` - Schema definition describing the table structure.
1176/// * `entry_count` - Entry count for root tables, or `None` for nested tables (reads u32 from data).
1177/// * `return_incomplete` - If `true`, returns partially decoded rows on field errors.
1178/// * `altered_flag` - Set to `true` if any data was modified during decoding.
1179///
1180/// # Returns
1181///
1182/// A vector of rows, where each row is a vector of `DecodedData` values.
1183pub(crate) fn decode_table<R: ReadBytes>(data: &mut R, definition: &Definition, entry_count: Option<u32>, return_incomplete: bool, altered_flag: &mut bool) -> Result<Vec<Vec<DecodedData>>> {
1184
1185    // If we received an entry count, it's the root table. If not, it's a nested one.
1186    let entry_count = match entry_count {
1187        Some(entry_count) => entry_count,
1188        None => data.read_u32()?,
1189    };
1190
1191    // Do not specify size here, because a badly written definition can end up triggering an OOM crash if we do.
1192    let fields = definition.fields();
1193    let mut table = if entry_count < 10_000 { Vec::with_capacity(entry_count as usize) } else { vec![] };
1194
1195    for row in 0..entry_count {
1196        table.push(decode_row(data, fields, row, return_incomplete, &Some(definition.patches()), altered_flag)?);
1197    }
1198
1199    Ok(table)
1200}
1201
1202/// Decodes a single row of table data from binary format.
1203///
1204/// Iterates through each field in the definition, decoding the binary data and applying
1205/// any necessary postprocessing (bitwise expansion, enum conversion, colour merging).
1206///
1207/// # Arguments
1208///
1209/// * `data` - Binary data source.
1210/// * `fields` - Field definitions for this row.
1211/// * `row` - Current row index (0-based, used for error messages).
1212/// * `return_incomplete` - If `true`, returns partial row data on field decode errors.
1213/// * `patches` - Optional definition patches to apply.
1214/// * `altered_flag` - Set to `true` if data was modified during decoding.
1215fn decode_row<R: ReadBytes>(data: &mut R, fields: &[Field], row: u32, return_incomplete: bool, patches: &Option<&DefinitionPatch>, altered_flag: &mut bool) -> Result<Vec<DecodedData>> {
1216    let mut split_colours: BTreeMap<u8, HashMap<String, u8>> = BTreeMap::new();
1217    let mut row_data = Vec::with_capacity(fields.len());
1218    for (column, field) in fields.iter().enumerate() {
1219
1220        // Decode the field, then apply any postprocess operation we need.
1221        let column = column as u32;
1222        let field_data = match decode_field(data, field, row, column, altered_flag) {
1223            Ok(data) => data,
1224            Err(error) => {
1225                if return_incomplete {
1226                    return Ok(row_data);
1227                } else {
1228                    return Err(error);
1229                }
1230            }
1231        };
1232        decode_field_postprocess(&mut row_data, field_data, field, &mut split_colours, patches, altered_flag)
1233    }
1234
1235    decode_row_postprocess(&mut row_data, &mut split_colours)?;
1236
1237    Ok(row_data)
1238}
1239
1240/// Decodes a single field from binary data based on its type.
1241///
1242/// Reads the appropriate number of bytes from the data source and converts them
1243/// to the corresponding `DecodedData` variant. String fields have special characters escaped.
1244///
1245/// # Arguments
1246///
1247/// * `data` - Binary data source.
1248/// * `field` - Field definition specifying the type to decode.
1249/// * `row` - Current row index (1-based in error messages).
1250/// * `column` - Current column index (1-based in error messages).
1251/// * `altered_flag` - Set to `true` if data was modified during decoding.
1252///
1253/// # Errors
1254///
1255/// Returns `RLibError::DecodingTableFieldError` if the field cannot be decoded,
1256/// or `RLibError::DecodingTableFieldSequenceDataError` for sequence field errors.
1257fn decode_field<R: ReadBytes>(data: &mut R, field: &Field, row: u32, column: u32, altered_flag: &mut bool) -> Result<DecodedData> {
1258    match field.field_type() {
1259        FieldType::Boolean => {
1260            data.read_bool()
1261                .map(DecodedData::Boolean)
1262                .map_err(|_| RLibError::DecodingTableFieldError(row + 1, column + 1, "Boolean".to_string()))
1263        }
1264        FieldType::F32 => {
1265            if let Ok(data) = data.read_f32() { Ok(DecodedData::F32(data)) }
1266            else { Err(RLibError::DecodingTableFieldError(row + 1, column + 1, "F32".to_string())) }
1267        }
1268        FieldType::F64 => {
1269            if let Ok(data) = data.read_f64() { Ok(DecodedData::F64(data)) }
1270            else { Err(RLibError::DecodingTableFieldError(row + 1, column + 1, "F64".to_string())) }
1271        }
1272        FieldType::I16 => {
1273            if let Ok(data) = data.read_i16() { Ok(DecodedData::I16(data))  }
1274            else { Err(RLibError::DecodingTableFieldError(row + 1, column + 1, "I16".to_string())) }
1275        }
1276        FieldType::I32 => {
1277            if let Ok(data) = data.read_i32() { Ok(DecodedData::I32(data)) }
1278            else { Err(RLibError::DecodingTableFieldError(row + 1, column + 1, "I32".to_string())) }
1279        }
1280        FieldType::I64 => {
1281            if let Ok(data) = data.read_i64() { Ok(DecodedData::I64(data)) }
1282            else { Err(RLibError::DecodingTableFieldError(row + 1, column + 1, "I64".to_string())) }
1283        }
1284        FieldType::ColourRGB => {
1285            if let Ok(data) = data.read_string_colour_rgb() { Ok(DecodedData::ColourRGB(data)) }
1286            else { Err(RLibError::DecodingTableFieldError(row + 1, column + 1, "Colour RGB".to_string())) }
1287        }
1288        FieldType::StringU8 => {
1289            if let Ok(mut data) = data.read_sized_string_u8() {
1290                escape_special_chars(&mut data);
1291                Ok(DecodedData::StringU8(data)) }
1292            else { Err(RLibError::DecodingTableFieldError(row + 1, column + 1, "UTF-8 String".to_string())) }
1293        }
1294        FieldType::StringU16 => {
1295            if let Ok(mut data) = data.read_sized_string_u16() {
1296                escape_special_chars(&mut data);
1297                Ok(DecodedData::StringU16(data)) }
1298            else { Err(RLibError::DecodingTableFieldError(row + 1, column + 1, "UTF-16 String".to_string())) }
1299        }
1300        FieldType::OptionalI16 => {
1301            if let Ok(data) = data.read_optional_i16() { Ok(DecodedData::OptionalI16(data)) }
1302            else { Err(RLibError::DecodingTableFieldError(row + 1, column + 1, "Optional I16".to_string())) }
1303        }
1304        FieldType::OptionalI32 => {
1305            if let Ok(data) = data.read_optional_i32() { Ok(DecodedData::OptionalI32(data)) }
1306            else { Err(RLibError::DecodingTableFieldError(row + 1, column + 1, "Optional I32".to_string())) }
1307        }
1308        FieldType::OptionalI64 => {
1309            if let Ok(data) = data.read_optional_i64() { Ok(DecodedData::OptionalI64(data)) }
1310            else { Err(RLibError::DecodingTableFieldError(row + 1, column + 1, "Optional I64".to_string())) }
1311        }
1312
1313        FieldType::OptionalStringU8 => {
1314            if let Ok(mut data) = data.read_optional_string_u8() {
1315                escape_special_chars(&mut data);
1316                Ok(DecodedData::OptionalStringU8(data)) }
1317            else { Err(RLibError::DecodingTableFieldError(row + 1, column + 1, "Optional UTF-8 String".to_string())) }
1318        }
1319        FieldType::OptionalStringU16 => {
1320            if let Ok(mut data) = data.read_optional_string_u16() {
1321                escape_special_chars(&mut data);
1322                Ok(DecodedData::OptionalStringU16(data)) }
1323            else { Err(RLibError::DecodingTableFieldError(row + 1, column + 1, "Optional UTF-16 String".to_string())) }
1324        }
1325
1326        FieldType::SequenceU16(definition) => {
1327            let start = data.stream_position()?;
1328            let entry_count = data.read_u16()?;
1329            match decode_table(data, definition, Some(entry_count as u32), false, altered_flag) {
1330                Ok(_) => {
1331                    let end = data.stream_position()? - start;
1332                    data.seek(SeekFrom::Start(start))?;
1333                    let blob = data.read_slice(end as usize, false)?;
1334                    Ok(DecodedData::SequenceU16(blob))
1335                }
1336                Err(error) => Err(RLibError::DecodingTableFieldSequenceDataError(row + 1, column + 1, error.to_string(), "SequenceU16".to_string()))
1337            }
1338        }
1339
1340        FieldType::SequenceU32(definition) => {
1341            let start = data.stream_position()?;
1342            let entry_count = data.read_u32()?;
1343            match decode_table(data, definition, Some(entry_count), false, altered_flag) {
1344                Ok(_) => {
1345                    let end = data.stream_position()? - start;
1346                    data.seek(SeekFrom::Start(start))?;
1347                    let blob = data.read_slice(end as usize, false)?;
1348                    Ok(DecodedData::SequenceU32(blob))
1349                }
1350                Err(error) => Err(RLibError::DecodingTableFieldSequenceDataError(row + 1, column + 1, error.to_string(), "SequenceU32".to_string()))
1351            }
1352        }
1353    }
1354}
1355
1356/// Applies row-level postprocessing after all fields have been decoded.
1357///
1358/// Merges split colour fields (r/g/b components stored as separate numeric fields)
1359/// into combined `ColourRGB` values. The merged colours are appended to the row data.
1360///
1361/// # Arguments
1362///
1363/// * `row_data` - The decoded row data to append merged colours to.
1364/// * `split_colours` - Map of colour group indices to their channel values (r/g/b).
1365///
1366/// # Errors
1367///
1368/// Returns `RLibError::DecodingTableCombinedColour` if colour values cannot be parsed.
1369fn decode_row_postprocess(row_data: &mut Vec<DecodedData>, split_colours: &mut BTreeMap<u8, HashMap<String, u8>>) -> Result<()> {
1370    for split_colour in split_colours.values() {
1371        let mut colour_hex = "".to_owned();
1372        if let Some(r) = split_colour.get("r") {
1373            colour_hex.push_str(&format!("{r:02X?}"));
1374        }
1375
1376        if let Some(r) = split_colour.get("red") {
1377            colour_hex.push_str(&format!("{r:02X?}"));
1378        }
1379
1380        if let Some(g) = split_colour.get("g") {
1381            colour_hex.push_str(&format!("{g:02X?}"));
1382        }
1383
1384        if let Some(g) = split_colour.get("green") {
1385            colour_hex.push_str(&format!("{g:02X?}"));
1386        }
1387
1388        if let Some(b) = split_colour.get("b") {
1389            colour_hex.push_str(&format!("{b:02X?}"));
1390        }
1391
1392        if let Some(b) = split_colour.get("blue") {
1393            colour_hex.push_str(&format!("{b:02X?}"));
1394        }
1395
1396        if u32::from_str_radix(&colour_hex, 16).is_ok() {
1397            row_data.push(DecodedData::ColourRGB(colour_hex));
1398        } else {
1399            return Err(RLibError::DecodingTableCombinedColour);
1400        }
1401    }
1402
1403    Ok(())
1404}
1405
1406/// Applies field-level postprocessing after decoding a single field.
1407///
1408/// Handles special field transformations:
1409/// - **Bitwise fields**: Expands integer into multiple boolean values based on bit positions.
1410/// - **Enum fields**: Converts integer values to their string representations.
1411/// - **Split colour fields**: Collects r/g/b components for later merging into `ColourRGB`.
1412/// - **Normal fields**: Adds the decoded data directly to the row.
1413///
1414/// # Arguments
1415///
1416/// * `row_data` - The row being built, receives the processed field data.
1417/// * `data` - The decoded field data to process.
1418/// * `field` - Field definition with metadata (bitwise count, enum values, colour group).
1419/// * `split_colours` - Accumulator for split colour field components.
1420/// * `_patches` - Definition patches (currently unused).
1421/// * `_altered_flag` - Modification flag (currently unused).
1422fn decode_field_postprocess(row_data: &mut Vec<DecodedData>, data: DecodedData, field: &Field, split_colours: &mut BTreeMap<u8, HashMap<String, u8>>, _patches: &Option<&DefinitionPatch>, _altered_flag: &mut bool) {
1423
1424    // If the field is a bitwise, split it into multiple fields. This is currently limited to integer types.
1425    if field.is_bitwise() > 1 {
1426        if [FieldType::I16, FieldType::I32, FieldType::I64].contains(field.field_type()) {
1427            let data = match data {
1428                DecodedData::I16(ref data) => *data as i64,
1429                DecodedData::I32(ref data) => *data as i64,
1430                DecodedData::I64(ref data) => *data,
1431                _ => unimplemented!()
1432            };
1433
1434            for bitwise_column in 0..field.is_bitwise() {
1435                row_data.push(DecodedData::Boolean(data & (1 << bitwise_column) != 0));
1436            }
1437        }
1438    }
1439
1440    // If the field has enum values, we turn it into a string. Same as before, only for integer types.
1441    else if !field.enum_values().is_empty() {
1442        if [FieldType::I16, FieldType::I32, FieldType::I64].contains(field.field_type()) {
1443            let data = match data {
1444                DecodedData::I16(ref data) => *data as i32,
1445                DecodedData::I32(ref data) => *data,
1446                DecodedData::I64(ref data) => *data as i32,
1447                _ => unimplemented!()
1448            };
1449            match field.enum_values().get(&data) {
1450                Some(data) => row_data.push(DecodedData::StringU8(data.to_owned())),
1451                None => row_data.push(DecodedData::StringU8(data.to_string()))
1452            }
1453        }
1454    }
1455
1456    // If the field is part of an split colour field group, don't add it. We'll separate it from the rest, then merge them into a ColourRGB field.
1457    else if let Some(colour_index) = field.is_part_of_colour() {
1458        if [FieldType::I16, FieldType::I32, FieldType::I64, FieldType::F32, FieldType::F64].contains(field.field_type()) {
1459            let data = match data {
1460                DecodedData::I16(ref data) => *data as u8,
1461                DecodedData::I32(ref data) => *data as u8,
1462                DecodedData::I64(ref data) => *data as u8,
1463                DecodedData::F32(ref data) => *data as u8,
1464                DecodedData::F64(ref data) => *data as u8,
1465                _ => unimplemented!()
1466            };
1467
1468            // This can be r, g, b, red, green, blue.
1469            let colour_split = field.name().rsplitn(2, '_').collect::<Vec<&str>>();
1470            let colour_channel = colour_split[0].to_lowercase();
1471            match split_colours.get_mut(&colour_index) {
1472                Some(colour_pack) => {
1473                    colour_pack.insert(colour_channel, data);
1474                }
1475                None => {
1476                    let mut colour_pack = HashMap::new();
1477                    colour_pack.insert(colour_channel, data);
1478                    split_colours.insert(colour_index, colour_pack);
1479                }
1480            }
1481        }
1482    }
1483    /*
1484    // Numeric fields are processed as i32. We need to write them back into their original type here.
1485    else if field.is_numeric(*patches) {
1486        let data = match data {
1487            DecodedData::I64(ref data) |
1488            DecodedData::OptionalI64(ref data) => *data as i32,
1489            DecodedData::StringU8(ref data) |
1490            DecodedData::StringU16(ref data) |
1491            DecodedData::OptionalStringU8(ref data) |
1492            DecodedData::OptionalStringU16(ref data) => match data.parse::<i32>() {
1493                Ok(data) => data,
1494                Err(_) => {
1495
1496                    // For what I could see, this happens when loading a table that has invalid data on the key field,
1497                    // which can happen accidentally when loading tables not intended for the game the definition is for,
1498                    // or regularly on tables with incorrectly inputted data. In those cases, we turn the value to 87654321
1499                    // and flag the table so it's know this has happened.
1500                    *altered_flag |= true;
1501                    87654321
1502                }
1503            },
1504            _ => unimplemented!()
1505        };
1506
1507        row_data.push(DecodedData::I32(data));
1508    }*/
1509
1510    else {
1511        row_data.push(data);
1512    }
1513}
1514
1515/// Encodes table data from decoded format back to binary.
1516///
1517/// Converts structured `DecodedData` rows back into binary format according to the
1518/// table definition. Handles special cases like bitwise fields, enum conversions,
1519/// and split colour fields.
1520///
1521/// # Arguments
1522///
1523/// * `entries` - Table rows to encode, each row is a vector of `DecodedData`.
1524/// * `data` - Output buffer implementing `WriteBytes`.
1525/// * `definition` - Schema definition describing the table structure.
1526/// * `patches` - Optional definition patches to apply.
1527///
1528/// # Special Handling
1529///
1530/// - **Split colours**: Extracts r/g/b values from merged `ColourRGB` fields.
1531/// - **Bitwise fields**: Combines consecutive boolean values into a single integer.
1532/// - **Enum fields**: Converts string values back to their integer keys.
1533/// - **Strings**: Unescapes special characters before writing.
1534///
1535/// # Errors
1536///
1537/// Returns an error if:
1538/// - Row has wrong number of fields.
1539/// - Field type doesn't match expected type for the column.
1540pub fn encode_table<W: WriteBytes>(entries: &[Vec<DecodedData>], data: &mut W, definition: &Definition, patches: &Option<&DefinitionPatch>) -> Result<()> {
1541
1542    // Get the table data in local format, no matter in what backend we stored it.
1543    let fields = definition.fields();
1544    let fields_processed = definition.fields_processed();
1545
1546    // Get the colour positions of the tables, if any.
1547    let combined_colour_positions = fields.iter().filter_map(|field| {
1548        if let Some(colour_group) = field.is_part_of_colour() {
1549            let colour_split = field.name().rsplitn(2, '_').collect::<Vec<&str>>();
1550            let colour_field_name: String = if colour_split.len() == 2 { format!("{}{}", colour_split[1].to_lowercase(), MERGE_COLOUR_POST) } else { format!("{}_{}", MERGE_COLOUR_NO_NAME.to_lowercase(), colour_group) };
1551
1552            definition.column_position_by_name(&colour_field_name).map(|x| (colour_field_name, x))
1553        } else { None }
1554    }).collect::<HashMap<String, usize>>();
1555
1556    for row in entries.iter() {
1557
1558        // First, we need to make sure we have the amount of fields we expected to be in the row.
1559        if row.len() != fields_processed.len() {
1560            return Err(RLibError::TableRowWrongFieldCount(fields_processed.len(), row.len()))
1561        }
1562
1563        // The way we process it is, we iterate over the definition fields (because it's what we need to write)
1564        // and write the fields getting only what we need from the table data.
1565        let mut data_column = 0;
1566        for field in fields {
1567
1568            // First special situation: join back split colour columns, if the field is a split colour.
1569            if let Some(colour_group) = field.is_part_of_colour() {
1570                let field_name = field.name().to_lowercase();
1571                let colour_split = field_name.rsplitn(2, '_').collect::<Vec<&str>>();
1572                let colour_channel = colour_split[0];
1573                let colour_field_name = if colour_split.len() == 2 {
1574                    format!("{}{}", colour_split[1], MERGE_COLOUR_POST)
1575                } else {
1576                    format!("{}_{}", MERGE_COLOUR_NO_NAME.to_lowercase(), colour_group)
1577                };
1578
1579                if let Some(data_column) = combined_colour_positions.get(&colour_field_name) {
1580                    match &row[*data_column] {
1581                        DecodedData::ColourRGB(field_data) => {
1582
1583                            // Encode the full colour, then grab the byte of our field.
1584                            let mut encoded = vec![];
1585                            encoded.write_string_colour_rgb(field_data)?;
1586
1587                            let field_data =
1588                                if colour_channel == "r" || colour_channel == "red" { encoded[2] }
1589                                else if colour_channel == "g" || colour_channel == "green" { encoded[1] }
1590                                else if colour_channel == "b" || colour_channel == "blue" { encoded[0] }
1591                            else { 0 };
1592
1593                            // Only these types can be split colours.
1594                            match field.field_type() {
1595                                FieldType::I16 => data.write_i16(field_data as i16)?,
1596                                FieldType::I32 => data.write_i32(field_data as i32)?,
1597                                FieldType::I64 => data.write_i64(field_data as i64)?,
1598                                FieldType::F32 => data.write_f32(field_data as f32)?,
1599                                FieldType::F64 => data.write_f64(field_data as f64)?,
1600                                _ => return Err(RLibError::EncodingTableWrongFieldType(FieldType::from(&row[*data_column]).to_string(), field.field_type().to_string()))
1601                            }
1602
1603
1604                        },
1605                        _ => return Err(RLibError::EncodingTableWrongFieldType(FieldType::from(&row[*data_column]).to_string(), field.field_type().to_string()))
1606                    }
1607                }
1608            }
1609
1610            // Second special situation: bitwise columns.
1611            else if field.is_bitwise() > 1 {
1612                let mut field_data: i64 = 0;
1613
1614                // Bitwise columns are always consecutive booleans.
1615                for bitwise_column in 0..field.is_bitwise() {
1616                    if let DecodedData::Boolean(boolean) = row[data_column] {
1617                        if boolean {
1618                            field_data |= 1 << bitwise_column;
1619                        }
1620                    }
1621
1622                    else {
1623                        return Err(RLibError::EncodingTableWrongFieldType(FieldType::from(&row[data_column]).to_string(), field.field_type().to_string()))
1624                    }
1625
1626                    data_column += 1;
1627                }
1628
1629                // Only integer types can be bitwise.
1630                match field.field_type() {
1631                    FieldType::I16 => data.write_i16(field_data as i16)?,
1632                    FieldType::I32 => data.write_i32(field_data as i32)?,
1633                    FieldType::I64 => data.write_i64(field_data)?,
1634                    _ => return Err(RLibError::EncodingTableWrongFieldType(FieldType::from(&row[data_column]).to_string(), field.field_type().to_string()))
1635                }
1636            }
1637            /*
1638            // Numeric fields are processed as i32. We need to write them back into their original type here.
1639            else if field.is_numeric(*patches) {
1640                match &row[data_column] {
1641                    DecodedData::I32(field_data) => {
1642                        match field.field_type() {
1643                            FieldType::I64 => data.write_i64(*field_data as i64)?,
1644                            FieldType::OptionalI64 => {
1645                                data.write_bool(true)?;
1646                                data.write_i64(*field_data as i64)?;
1647                            },
1648                            FieldType::StringU8 => data.write_sized_string_u8(&field_data.to_string())?,
1649                            FieldType::StringU16 => data.write_sized_string_u16(&field_data.to_string())?,
1650                            FieldType::OptionalStringU8 => data.write_optional_string_u8(&field_data.to_string())?,
1651                            FieldType::OptionalStringU16 => data.write_optional_string_u16(&field_data.to_string())?,
1652                            _ => return Err(RLibError::EncodingTableWrongFieldType(field_data.to_string(), field.field_type().to_string())),
1653                        }
1654                    }
1655                    _ => return Err(RLibError::EncodingTableWrongFieldType(FieldType::from(&row[data_column]).to_string(), field.field_type().to_string())),
1656                }
1657            }*/
1658
1659            // If no special behavior has been needed, encode the field as a normal field, except for strings.
1660            else {
1661
1662                match &row[data_column] {
1663                    DecodedData::Boolean(field_data) => data.write_bool(*field_data)?,
1664                    DecodedData::F32(field_data) => data.write_f32(*field_data)?,
1665                    DecodedData::F64(field_data) => data.write_f64(*field_data)?,
1666                    DecodedData::I16(field_data) => data.write_i16(*field_data)?,
1667                    DecodedData::I32(field_data) => data.write_i32(*field_data)?,
1668                    DecodedData::I64(field_data) => data.write_i64(*field_data)?,
1669                    DecodedData::ColourRGB(field_data) => data.write_string_colour_rgb(field_data)?,
1670                    DecodedData::OptionalI16(field_data) => {
1671                        data.write_bool(true)?;
1672                        data.write_i16(*field_data)?
1673                    },
1674                    DecodedData::OptionalI32(field_data) => {
1675                        data.write_bool(true)?;
1676                        data.write_i32(*field_data)?
1677                    },
1678                    DecodedData::OptionalI64(field_data) => {
1679                        data.write_bool(true)?;
1680                        data.write_i64(*field_data)?
1681                    },
1682
1683                    // String fields may need preprocessing applied to them before encoding.
1684                    DecodedData::StringU8(field_data) |
1685                    DecodedData::StringU16(field_data) |
1686                    DecodedData::OptionalStringU8(field_data) |
1687                    DecodedData::OptionalStringU16(field_data) => {
1688
1689                        // String files may be representations of enums (as integer => string) for ease of use.
1690                        // If so, we need to find the underlying integer key of our string and encode that.
1691                        if !field.enum_values().is_empty() {
1692                            let field_data = match field.enum_values()
1693                                .iter()
1694                                .find_map(|(x, y)|
1695                                    if y.to_lowercase() == field_data.to_lowercase() { Some(x) } else { None }) {
1696                                Some(value) => {
1697                                    match field.field_type() {
1698                                        FieldType::I16 => DecodedData::I16(*value as i16),
1699                                        FieldType::I32 => DecodedData::I32(*value),
1700                                        FieldType::I64 => DecodedData::I64(*value as i64),
1701                                        _ => return Err(RLibError::EncodingTableWrongFieldType(field_data.to_string(), field.field_type().to_string()))
1702                                    }
1703                                }
1704                                None => match row[data_column].convert_between_types(field.field_type()) {
1705                                    Ok(data) => data,
1706                                    Err(_) => {
1707                                        let default_value = field.default_value(*patches);
1708                                        DecodedData::new_from_type_and_value(field.field_type(), &default_value)
1709                                    }
1710                                }
1711                            };
1712
1713                            // If there are no problems, encode the data.
1714                            match field_data {
1715                                DecodedData::I16(field_data) => data.write_i16(field_data)?,
1716                                DecodedData::I32(field_data) => data.write_i32(field_data)?,
1717                                DecodedData::I64(field_data) => data.write_i64(field_data)?,
1718                                _ => return Err(RLibError::EncodingTableWrongFieldType(field_data.data_to_string().to_string(), field.field_type().to_string()))
1719                            }
1720                        }
1721                        else {
1722
1723                            // If there are no problems, encode the data.
1724                            match field.field_type() {
1725                                FieldType::StringU8 => data.write_sized_string_u8(&unescape_special_chars(field_data))?,
1726                                FieldType::StringU16 => data.write_sized_string_u16(&unescape_special_chars(field_data))?,
1727                                FieldType::OptionalStringU8 => data.write_optional_string_u8(&unescape_special_chars(field_data))?,
1728                                FieldType::OptionalStringU16 => data.write_optional_string_u16(&unescape_special_chars(field_data))?,
1729                                _ => return Err(RLibError::EncodingTableWrongFieldType(field_data.to_string(), field.field_type().to_string()))
1730                            }
1731                        }
1732                    }
1733
1734                    // Make sure we at least have the counter before writing. We need at least that.
1735                    DecodedData::SequenceU16(field_data) => {
1736                        if field_data.len() < 2 {
1737                            data.write_all(&[0, 0])?
1738                        } else {
1739                            data.write_all(field_data)?
1740                        }
1741                    },
1742                    DecodedData::SequenceU32(field_data) => {
1743                        if field_data.len() < 4 {
1744                            data.write_all(&[0, 0, 0, 0])?
1745                        } else {
1746                            data.write_all(field_data)?
1747                        }
1748                    }
1749                }
1750
1751                data_column += 1;
1752            }
1753        }
1754    }
1755
1756    Ok(())
1757}