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}