rpfm_lib/schema/
mod.rs

1//---------------------------------------------------------------------------//
2// Copyright (c) 2017-2024 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/*!
12Module with all the code to interact with Schemas.
13
14This module contains all the code related with the schemas used by this lib to decode many PackedFile types.
15
16The basic structure of an `Schema` is:
17```ignore
18(
19    version: 3,
20    versioned_files: [
21        DB("_kv_battle_ai_ability_usage_variables_tables", [
22            (
23                version: 0,
24                fields: [
25                    (
26                        name: "key",
27                        field_type: StringU8,
28                        is_key: true,
29                        default_value: None,
30                        is_filename: false,
31                        filename_relative_path: None,
32                        is_reference: None,
33                        lookup: None,
34                        description: "",
35                        ca_order: -1,
36                        is_bitwise: 0,
37                        enum_values: {},
38                        is_part_of_colour: None,
39                    ),
40                    (
41                        name: "value",
42                        field_type: F32,
43                        is_key: false,
44                        default_value: None,
45                        is_filename: false,
46                        filename_relative_path: None,
47                        is_reference: None,
48                        lookup: None,
49                        description: "",
50                        ca_order: -1,
51                        is_bitwise: 0,
52                        enum_values: {},
53                        is_part_of_colour: None,
54                    ),
55                ],
56                localised_fields: [],
57            ),
58        ]),
59    ],
60)
61```
62
63Inside the schema there are `VersionedFile` variants of different types, with a Vec of `Definition`, one for each version of that PackedFile supported.
64!*/
65
66use getset::*;
67use itertools::Itertools;
68use rayon::prelude::*;
69use ron::de::{from_bytes, from_str};
70use ron::ser::{to_string_pretty, PrettyConfig};
71use serde::{Serialize as SerdeSerialize, Serializer};
72use serde_derive::{Serialize, Deserialize};
73
74use std::cmp::Ordering;
75use std::collections::{BTreeMap, HashMap};
76use std::{fmt, fmt::Display};
77use std::fs::{DirBuilder, File};
78use std::io::{BufReader, BufWriter, Read, Write};
79use std::path::Path;
80
81#[cfg(feature = "integration_assembly_kit")]use crate::integrations::assembly_kit::localisable_fields::RawLocalisableField;
82#[cfg(feature = "integration_assembly_kit")]use crate::integrations::assembly_kit::table_definition::RawDefinition;
83#[cfg(feature = "integration_assembly_kit")]use crate::integrations::assembly_kit::table_definition::RawField;
84#[cfg(feature = "integration_log")] use crate::integrations::log::*;
85#[cfg(feature = "integration_sqlite")] use rusqlite::types::Type;
86
87use crate::error::Result;
88use crate::files::table::DecodedData;
89use crate::games::supported_games::SupportedGames;
90
91// Legacy Schemas, to keep backwards compatibility during updates.
92pub(crate) mod v4;
93
94/// Name of the folder containing all the schemas.
95pub const SCHEMA_FOLDER: &str = "schemas";
96
97//const BINARY_EXTENSION: &str = ".bin";
98
99pub const SCHEMA_REPO: &str = "https://github.com/Frodo45127/rpfm-schemas";
100pub const SCHEMA_REMOTE: &str = "origin";
101pub const SCHEMA_BRANCH: &str = "master";
102
103/// Current structural version of the Schema, for compatibility purposes.
104const CURRENT_STRUCTURAL_VERSION: u16 = 5;
105const INVALID_VERSION: i32 = -100;
106
107/// Name for unamed colour groups.
108pub const MERGE_COLOUR_NO_NAME: &str = "Unnamed Colour Group";
109
110/// Ending for named colour groups.
111pub const MERGE_COLOUR_POST: &str = "_hex";
112
113/// Fields that can be ignored in missing field checks.
114const IGNORABLE_FIELDS: [&str; 4] = ["s_ColLineage", "s_Generation", "s_GUID", "s_Lineage"];
115
116//---------------------------------------------------------------------------//
117//                              Enum & Structs
118//---------------------------------------------------------------------------//
119
120/// This type defines patches for specific table definitions, in a ColumnName -> [key -> value] format.
121///
122/// Note: for table-wide patches, we use column name "-1".
123pub type DefinitionPatch = HashMap<String, HashMap<String, String>>;
124
125/// This struct represents a Schema File in memory, ready to be used to decode versioned PackedFiles.
126#[derive(Clone, PartialEq, Eq, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
127#[getset(get = "pub", get_mut = "pub", set = "pub")]
128pub struct Schema {
129
130    /// It stores the structural version of the Schema.
131    version: u16,
132
133    /// It stores the versioned files inside the Schema.
134    #[serde(serialize_with = "ordered_map_definitions")]
135    definitions: HashMap<String, Vec<Definition>>,
136
137    /// It stores a list of per-table, per-column patches.
138    #[serde(serialize_with = "ordered_map_patches")]
139    patches: HashMap<String, DefinitionPatch>,
140}
141
142/// This struct contains all the data needed to decode a specific version of a versioned PackedFile.
143#[derive(Clone, PartialEq, Eq, Debug, Default, Getters, MutGetters, Setters, Serialize, Deserialize)]
144#[getset(get = "pub", get_mut = "pub", set = "pub")]
145pub struct Definition {
146
147    /// The version of the file the definition is for. These versions are:
148    /// - `-1`: for fake `Definition`, used for dependency resolving stuff.
149    /// - `0`: for unversioned PackedFiles.
150    /// - `1+`: for versioned PackedFiles.
151    version: i32,
152
153    /// This is a collection of all `Field`s the PackedFile uses, in the order it uses them.
154    fields: Vec<Field>,
155
156    /// This is a list of all the fields from this definition that are moved to a Loc PackedFile on exporting.
157    localised_fields: Vec<Field>,
158
159    /// This is the order in which key fields are ordered when used as part of a localisation key.
160    ///
161    /// Note: this order is only applicable to the processed fields, not the raw fields.
162    localised_key_order: Vec<u32>,
163
164    /// Patches that apply to this definition.
165    ///
166    /// Not serialized. Populated when getting the definition.
167    #[serde(skip)]
168    patches: DefinitionPatch
169}
170
171/// This struct holds all the relevant data do properly decode a field from a versioned PackedFile.
172#[derive(Clone, PartialEq, Eq, Debug, Setters, Serialize, Deserialize)]
173#[getset(set = "pub")]
174pub struct Field {
175
176    /// Name of the field. Should contain no spaces, using `_` instead.
177    name: String,
178
179    /// Type of the field.
180    field_type: FieldType,
181
182    /// `True` if the field is a `Key` field of a table. `False` otherwise.
183    is_key: bool,
184
185    /// The default value of the field.
186    default_value: Option<String>,
187
188    /// If the field's data corresponds to a filename.
189    is_filename: bool,
190
191    /// Path where the file in the data of the field can be, if it's restricted to one path.
192    filename_relative_path: Option<String>,
193
194    /// `Some(referenced_table, referenced_column)` if the field is referencing another table/column. `None` otherwise.
195    is_reference: Option<(String, String)>,
196
197    /// `Some(referenced_columns)` if the field is using another column/s from the referenced table for lookup values.
198    lookup: Option<Vec<String>>,
199
200    /// Aclarative description of what the field is for.
201    description: String,
202
203    /// Visual position in CA's Table. `-1` means we don't know its position.
204    ca_order: i16,
205
206    /// Variable to tell if this column is a bitwise column (spanned accross multiple columns) or not. Only applicable to numeric fields.
207    is_bitwise: i32,
208
209    /// Variable that specifies the "Enum" values for each value in this field.
210    enum_values: BTreeMap<i32, String>,
211
212    /// If the field is part of a 3-part RGB column set, and which one (R, G or B) it is.
213    is_part_of_colour: Option<u8>,
214
215    /// If the field is not used by the game. This one is set through patches, so we don't serialize it.
216    #[serde(skip_serializing, skip_deserializing)]
217    unused: bool,
218}
219
220/// This enum defines every type of field the lib can encode/decode.
221#[derive(Clone, PartialEq, Eq, Debug, Serialize, Deserialize)]
222pub enum FieldType {
223    Boolean,
224    F32,
225    F64,
226    I16,
227    I32,
228    I64,
229    ColourRGB,
230    StringU8,
231    StringU16,
232    OptionalI16,
233    OptionalI32,
234    OptionalI64,
235    OptionalStringU8,
236    OptionalStringU16,
237    SequenceU16(Box<Definition>),
238    SequenceU32(Box<Definition>)
239}
240
241//---------------------------------------------------------------------------//
242//                       Enum & Structs Implementations
243//---------------------------------------------------------------------------//
244
245/// Implementation of `Schema`.
246impl Schema {
247
248    /// This function will save a new patch to the local patches list.
249    pub fn new_patch(patches: &HashMap<String, DefinitionPatch>, path: &Path) -> Result<()> {
250        let mut file = BufReader::new(File::open(path)?);
251        let mut data = Vec::with_capacity(file.get_ref().metadata()?.len() as usize);
252        file.read_to_end(&mut data)?;
253        let mut local_patches: HashMap<String, DefinitionPatch> = from_bytes(&data)?;
254
255        Self::add_patch_to_patch_set(&mut local_patches, patches);
256
257        let mut file = BufWriter::new(File::create(path)?);
258        let config = PrettyConfig::default();
259        file.write_all(to_string_pretty(&local_patches, config)?.as_bytes())?;
260
261        Ok(())
262    }
263
264    /// This function will remove the local patches for the specified table.
265    pub fn remove_patch_for_table(table_name: &str, path: &Path) -> Result<()> {
266        let mut file = BufReader::new(File::open(path)?);
267        let mut data = Vec::with_capacity(file.get_ref().metadata()?.len() as usize);
268        file.read_to_end(&mut data)?;
269        let mut local_patches: HashMap<String, DefinitionPatch> = from_bytes(&data)?;
270
271        local_patches.remove(table_name);
272
273        let mut file = BufWriter::new(File::create(path)?);
274        let config = PrettyConfig::default();
275        file.write_all(to_string_pretty(&local_patches, config)?.as_bytes())?;
276
277        Ok(())
278    }
279
280    /// This function will remove the local patches for the specified table and field.
281    pub fn remove_patch_for_field(table_name: &str, field_name: &str, path: &Path) -> Result<()> {
282        let mut file = BufReader::new(File::open(path)?);
283        let mut data = Vec::with_capacity(file.get_ref().metadata()?.len() as usize);
284        file.read_to_end(&mut data)?;
285        let mut local_patches: HashMap<String, DefinitionPatch> = from_bytes(&data)?;
286
287        if let Some(table_patches) = local_patches.get_mut(table_name) {
288            table_patches.remove(field_name);
289        }
290
291        let mut file = BufWriter::new(File::create(path)?);
292        let config = PrettyConfig::default();
293        file.write_all(to_string_pretty(&local_patches, config)?.as_bytes())?;
294
295        Ok(())
296    }
297
298    /// This function retrieves a value from a patch for a specific table, column and key.
299    pub fn patch_value(&self, table_name: &str, column_name: &str, key: &str) -> Option<&String> {
300        self.patches.get(table_name)?.get(column_name)?.get(key)
301    }
302
303    /// This function retrieves all patches that affect a specific table.
304    pub fn patches_for_table(&self, table_name: &str) -> Option<&DefinitionPatch> {
305        self.patches.get(table_name)
306    }
307
308    /// This function adds a list of patches into the currently loaded schema.
309    ///
310    /// Note: if you add a patch, you'll need to re-retrieve any definition you retrieved before in order for them to get patched.
311    pub fn add_patch_to_patch_set(patch_set: &mut HashMap<String, DefinitionPatch>, patches: &HashMap<String, DefinitionPatch>) {
312        patches.iter().for_each(|(table_name, column_patch)| {
313            match patch_set.get_mut(table_name) {
314                Some(column_patch_current) => {
315                    column_patch.iter().for_each(|(column_name, patch)| {
316                        match column_patch_current.get_mut(column_name) {
317                            Some(patch_current) => patch_current.extend(patch.clone()),
318                            None => {
319                                column_patch_current.insert(column_name.to_owned(), patch.clone());
320                            }
321                        }
322                    });
323                }
324                None => {
325                    patch_set.insert(table_name.to_owned(), column_patch.clone());
326                }
327            }
328        });
329    }
330
331    /// This function adds a definition for a table into the currently loaded schema.
332    pub fn add_definition(&mut self, table_name: &str, definition: &Definition) {
333        match self.definitions.get_mut(table_name) {
334            Some(definitions) => {
335                match definitions.iter_mut().find(|def| def.version() == definition.version()) {
336                    Some(def) => *def = definition.to_owned(),
337                    None => definitions.push(definition.to_owned()),
338                }
339            },
340            None => { self.definitions.insert(table_name.to_owned(), vec![definition.to_owned()]); },
341        }
342    }
343
344    /// This function removes a definition for a table from the currently loaded schema.
345    pub fn remove_definition(&mut self, table_name: &str, version: i32) {
346        if let Some(definitions) = self.definitions.get_mut(table_name) {
347            let mut index_to_delete = vec![];
348            for (index, definition) in definitions.iter().enumerate() {
349                if definition.version == version {
350                    index_to_delete.push(index);
351                }
352            }
353
354            index_to_delete.iter().rev().for_each(|index| { definitions.remove(*index); });
355        }
356    }
357
358    /// This function returns a copy of a specific `VersionedFile` of DB Type from the provided `Schema`.
359    pub fn definitions_by_table_name_cloned(&self, table_name: &str) -> Option<Vec<Definition>> {
360        self.definitions.get(table_name).cloned()
361    }
362
363    /// This function returns a reference to a specific `VersionedFile` of DB Type from the provided `Schema`.
364    pub fn definitions_by_table_name(&self, table_name: &str) -> Option<&Vec<Definition>>  {
365        self.definitions.get(table_name)
366    }
367
368    /// This function returns a mutable reference to a specific `VersionedFile` of DB Type from the provided `Schema`.
369    pub fn definitions_by_table_name_mut(&mut self, table_name: &str) -> Option<&mut Vec<Definition>>  {
370        self.definitions.get_mut(table_name)
371    }
372
373    /// This function returns the last compatible definition of a DB Table.
374    ///
375    /// As we may have versions from other games, we first need to check for the last definition in the dependency database.
376    /// If that fails, we try to get it from the schema.
377    pub fn definition_newer(&self, table_name: &str, candidates: &[Definition]) -> Option<&Definition> {
378
379        // Version is... complicated. We don't really want the last one, but the last one compatible with our game.
380        // So we have to try to get it first from the Dependency Database first. If that fails, we fall back to the schema.
381        if let Some(definition) = candidates.iter().max_by(|x, y| x.version().cmp(y.version())) {
382            self.definition_by_name_and_version(table_name, *definition.version())
383        }
384
385        // If there was no coincidence in the dependency database... we risk ourselves getting the last definition we have for
386        // that db from the schema.
387        else{
388            self.definitions.get(table_name)?.first()
389        }
390    }
391
392    pub fn definition_by_name_and_version(&self, table_name: &str, table_version: i32) -> Option<&Definition>  {
393        self.definitions.get(table_name)?.iter().find(|definition| *definition.version() == table_version)
394    }
395
396    pub fn definition_by_name_and_version_mut(&mut self, table_name: &str, table_version: i32) -> Option<&mut Definition>  {
397        self.definitions.get_mut(table_name)?.iter_mut().find(|definition| *definition.version() == table_version)
398    }
399
400    /// This function loads a [Schema] to memory from a provided `.ron` file.
401    pub fn load(path: &Path, local_patches: Option<&Path>) -> Result<Self> {
402        let mut file = BufReader::new(File::open(path)?);
403        let mut data = Vec::with_capacity(file.get_ref().metadata()?.len() as usize);
404        file.read_to_end(&mut data)?;
405        let mut schema: Self = from_bytes(&data)?;
406        let mut patches = schema.patches().clone();
407
408        // If we got local patches, add them to the patches list.
409        //
410        // NOTE: we separate the patches from the schemas because otherwise an schema edit will save local patches into the schema,
411        // and we want them to remain local.
412        if let Some(path) = local_patches {
413            if let Ok(file) = File::open(path) {
414                let mut file = BufReader::new(file);
415                let mut data = Vec::with_capacity(file.get_ref().metadata()?.len() as usize);
416                file.read_to_end(&mut data)?;
417                if let Ok(local_patches) = from_bytes::<HashMap<String, DefinitionPatch>>(&data) {
418                    Self::add_patch_to_patch_set(&mut patches, &local_patches);
419                }
420            }
421        }
422
423        // Preload all patches to their respective definitions.
424        for (table_name, patches) in &patches {
425            if let Some(definitions) = schema.definitions_by_table_name_mut(table_name) {
426                for definition in definitions {
427                    definition.set_patches(patches.clone());
428                }
429            }
430        }
431
432        Ok(schema)
433    }
434
435    /// This function loads a [Schema] to memory from a provided `.json` file.
436    pub fn load_json(path: &Path) -> Result<Self> {
437        let mut file = BufReader::new(File::open(path)?);
438        let mut data = Vec::with_capacity(file.get_ref().metadata()?.len() as usize);
439        file.read_to_end(&mut data)?;
440        let mut schema: Self = serde_json::from_slice(&data)?;
441
442        // Preload all patches to their respective definitions.
443        for (table_name, patches) in schema.patches().clone() {
444            if let Some(definitions) = schema.definitions_by_table_name_mut(&table_name) {
445                for definition in definitions {
446                    definition.set_patches(patches.clone());
447                }
448            }
449        }
450
451        Ok(schema)
452    }
453
454    /// This function saves a [Schema] from memory to a `.ron` file with the provided path.
455    pub fn save(&mut self, path: &Path) -> Result<()> {
456
457        // Make sure the path exists to avoid problems with updating schemas.
458        if let Some(parent_folder) = path.parent() {
459            DirBuilder::new().recursive(true).create(parent_folder)?;
460        }
461
462        let mut file = BufWriter::new(File::create(path)?);
463        let config = PrettyConfig::default();
464
465        let mut patches = HashMap::new();
466
467        // Make sure all definitions are properly sorted by version number.
468        self.definitions.iter_mut().for_each(|(table_name, definitions)| {
469            definitions.sort_by(|a, b| b.version().cmp(a.version()));
470
471            // Fix for empty dependencies, again.
472            definitions.iter_mut().for_each(|definition| {
473                definition.fields.iter_mut().for_each(|field| {
474                    if let Some((ref_table, ref_column)) = field.is_reference(None) {
475                        if ref_table.trim().is_empty() || ref_column.trim().is_empty() {
476                            field.is_reference = None;
477                        }
478                    }
479                });
480
481                // Move any lookup_hardcoded patches to schema patches.
482                if definition.patches.values().any(|x| x.keys().any(|y| y == "lookup_hardcoded")) {
483                    let mut def_patches = definition.patches().clone();
484                    def_patches.retain(|_, value| {
485                        value.retain(|key, _| key == "lookup_hardcoded");
486                        !value.is_empty()
487                    });
488                    patches.insert(table_name.to_owned(), def_patches);
489                }
490
491                // Move any unused patches to schema patches.
492                if definition.patches.values().any(|x| x.keys().any(|y| y == "unused")) {
493                    let mut def_patches = definition.patches().clone();
494                    def_patches.retain(|_, value| {
495                        value.retain(|key, _| key == "unused");
496                        !value.is_empty()
497                    });
498                    patches.insert(table_name.to_owned(), def_patches);
499                }
500            })
501        });
502
503        Self::add_patch_to_patch_set(self.patches_mut(), &patches);
504
505        file.write_all(to_string_pretty(&self, config)?.as_bytes())?;
506        Ok(())
507    }
508
509    /// This function saves a [Schema] from memory to a `.json` file with the provided path.
510    pub fn save_json(&mut self, path: &Path) -> Result<()> {
511        let mut path = path.to_path_buf();
512        path.set_extension("json");
513
514        // Make sure the path exists to avoid problems with updating schemas.
515        if let Some(parent_folder) = path.parent() {
516            DirBuilder::new().recursive(true).create(parent_folder)?;
517        }
518
519        let mut file = BufWriter::new(File::create(&path)?);
520
521        // Make sure all definitions are properly sorted by version number.
522        self.definitions.iter_mut().for_each(|(_, definitions)| {
523            definitions.sort_by(|a, b| b.version().cmp(a.version()));
524        });
525
526        file.write_all(serde_json::to_string_pretty(&self)?.as_bytes())?;
527        Ok(())
528    }
529
530    /// This function exports all the schema files from the provided folder to `.json`.
531    ///
532    /// For compatibility purposes.
533    pub fn export_to_json(schema_folder_path: &Path) -> Result<()> {
534        let games = SupportedGames::default();
535
536        games.games_sorted().par_iter().map(|x| x.schema_file_name()).try_for_each(|schema_file| {
537            let mut schema_path = schema_folder_path.to_owned();
538            schema_path.push(schema_file);
539
540            let mut schema = Schema::load(&schema_path, None)?;
541            schema_path.set_extension("json");
542            schema.save_json(&schema_path)?;
543            Ok(())
544        })
545    }
546
547    /*
548    /// This function exports all the schema files from the `schemas/` folder to `.xml`.
549    ///
550    /// For compatibility purposes.
551    pub fn export_to_xml() -> Result<()> {
552        for schema_file in SUPPORTED_GAMES.get_games().iter().map(|x| x.get_schema_name()) {
553            let schema = Schema::load(schema_file)?;
554
555            let mut file_path = get_config_path()?.join(SCHEMA_FOLDER);
556            file_path.push(schema_file);
557            file_path.set_extension("xml");
558
559            let mut file = File::create(&file_path)?;
560            file.write_all(quick_xml::se::to_string(&schema)?.as_bytes())?;
561        }
562        Ok(())
563    }
564*/
565
566    /// This function allow us to update the provided Schema from a legacy format into the current one.
567    pub fn update(schema_path: &Path, schema_patches_path: &Path, game_name: &str) -> Result<()>{
568        v4::SchemaV4::update(schema_path, schema_patches_path, game_name)
569    }
570
571    /// This function returns all columns that reference the columns on our specific table within the DB Tables of our Schema.
572    ///
573    /// Returns a list of (local_column_name, vec<(remote_table_name, remote_column_name)>).
574    pub fn referencing_columns_for_table(&self, table_name: &str, definition: &Definition) -> HashMap<String, HashMap<String, Vec<String>>> {
575
576        // Iterate over all definitions and find the ones referencing our table/field.
577        let fields_processed = definition.fields_processed();
578        let definitions = self.definitions();
579        let table_name_no_tables = table_name.to_owned().drain(..table_name.len() - 7).collect::<String>();
580
581        fields_processed.iter().filter_map(|field| {
582
583            let references = definitions.par_iter().filter_map(|(ver_name, ver_definitions)| {
584                let mut references = ver_definitions.iter().filter_map(|ver_definition| {
585                    let ver_patches = Some(ver_definition.patches());
586                    let references = ver_definition.fields_processed().iter().filter_map(|ver_field| {
587                        if let Some((source_table_name, source_column_name)) = ver_field.is_reference(ver_patches) {
588                            if table_name_no_tables == source_table_name && field.name() == source_column_name {
589                                Some(ver_field.name().to_owned())
590                            } else { None }
591                        } else { None }
592                    }).collect::<Vec<String>>();
593                    if references.is_empty() {
594                        None
595                    } else {
596                        Some(references)
597                    }
598                }).flatten().collect::<Vec<String>>();
599                if references.is_empty() {
600                    None
601                } else {
602                    references.sort();
603                    references.dedup();
604                    Some((ver_name.to_owned(), references))
605                }
606            }).collect::<HashMap<String, Vec<String>>>();
607            if references.is_empty() {
608                None
609            } else {
610                Some((field.name().to_owned(), references))
611            }
612        }).collect()
613    }
614
615    /// This function returns the list of table/columns that reference the provided columns,
616    /// and if there may be a loc entry that changing our column may need a change.
617    ///
618    /// This supports more than one reference level, except for locs.
619    pub fn tables_and_columns_referencing_our_own(
620        &self,
621        table_name: &str,
622        column_name: &str,
623        fields: &[Field],
624        localised_fields: &[Field]
625    ) -> (BTreeMap<String, Vec<String>>, bool) {
626
627        // Make sure the table name is correct.
628        let short_table_name = if table_name.ends_with("_tables") { table_name.split_at(table_name.len() - 7).0 } else { table_name };
629        let mut tables: BTreeMap<String, Vec<String>> = BTreeMap::new();
630
631        // We get all the db definitions from the schema, then iterate all of them to find what tables/columns reference our own.
632        for (ref_table_name, ref_definition) in self.definitions() {
633            let mut columns: Vec<String> = vec![];
634            for ref_version in ref_definition {
635                let ref_fields = ref_version.fields_processed();
636                let ref_patches = Some(ref_version.patches());
637                let ref_fields_localised = ref_version.localised_fields();
638                for ref_field in &ref_fields {
639                    if let Some((ref_ref_table, ref_ref_field)) = ref_field.is_reference(ref_patches) {
640
641                        // As this applies to all versions of a table, skip repeated fields.
642                        if ref_ref_table == short_table_name && ref_ref_field == column_name && !columns.iter().any(|x| x == ref_field.name()) {
643                            columns.push(ref_field.name().to_owned());
644
645                            // If we find a referencing column, get recursion working to check if there is any column referencing this one that needs to be edited.
646                            let (ref_of_ref, _) = self.tables_and_columns_referencing_our_own(ref_table_name, ref_field.name(), &ref_fields, ref_fields_localised);
647                            for refs in &ref_of_ref {
648                                match tables.get_mut(refs.0) {
649                                    Some(columns) => for value in refs.1 {
650                                        if !columns.contains(value) {
651                                            columns.push(value.to_owned());
652                                        }
653                                    }
654                                    None => { tables.insert(refs.0.to_owned(), refs.1.to_vec()); },
655                                }
656                            }
657                        }
658                    }
659                }
660            }
661
662            // Only add them if we actually found columns.
663            if !columns.is_empty() {
664                tables.insert(ref_table_name.to_owned(), columns);
665            }
666        }
667
668        // Also, check if we have to be careful about localised fields.
669        let patches = self.patches().get(table_name);
670        let has_loc_fields = if let Some(field) = fields.iter().find(|x| x.name() == column_name) {
671            (field.is_key(patches) || field.name() == "key") && !localised_fields.is_empty()
672        } else { false };
673
674        (tables, has_loc_fields)
675    }
676    /// This function tries to load multiple patches from a str.
677    pub fn load_patches_from_str(patch: &str) -> Result<HashMap<String, DefinitionPatch>> {
678        from_str(patch).map_err(From::from)
679    }
680
681    /// This function tries to load multiple definitions from a str.
682    pub fn load_definitions_from_str(definition: &str) -> Result<HashMap<String, Definition>> {
683        from_str(definition).map_err(From::from)
684    }
685
686    /// This function tries to export a list of patches to a ron string.
687    pub fn export_patches_to_str(patches: &HashMap<String, DefinitionPatch>) -> Result<String> {
688        let config = PrettyConfig::default();
689        ron::ser::to_string_pretty(&patches, config).map_err(From::from)
690    }
691
692    /// This function tries to export a list of definitions to a ron string.
693    pub fn export_definitions_to_str(definitions: &HashMap<String, Definition>) -> Result<String> {
694        let config = PrettyConfig::default();
695        ron::ser::to_string_pretty(&definitions, config).map_err(From::from)
696    }
697
698    /// This function tries to upload a bunch of [DefinitionPatch] to Sentry's service.
699    ///
700    /// It requires the **integration_log** feature.
701    #[cfg(feature = "integration_log")]
702    pub fn upload_patches(sentry_guard: &ClientInitGuard, game_name: &str, patches: HashMap<String, DefinitionPatch>) -> Result<()> {
703        let level = Level::Info;
704        let message = format!("Schema Patch for: {} - {}.", game_name, crate::utils::current_time()?);
705        let config = PrettyConfig::default();
706        let mut data = vec![];
707        ron::ser::to_writer_pretty(&mut data, &patches, config)?;
708        let file_name = "patch.txt";
709
710        Logger::send_event(sentry_guard, level, &message, Some((file_name, &data)))
711    }
712
713    /// This function tries to upload a bunch of [Definition] to Sentry's service.
714    ///
715    /// It requires the **integration_log** feature.
716    #[cfg(feature = "integration_log")]
717    pub fn upload_definitions(sentry_guard: &ClientInitGuard, game_name: &str, definitions: HashMap<String, Definition>) -> Result<()> {
718        let level = Level::Info;
719        let message = format!("Schema Definition for: {} - {}.", game_name, crate::utils::current_time()?);
720        let config = PrettyConfig::default();
721        let mut data = vec![];
722        ron::ser::to_writer_pretty(&mut data, &definitions, config)?;
723        let file_name = "definition.txt";
724
725        Logger::send_event(sentry_guard, level, &message, Some((file_name, &data)))
726    }
727}
728
729impl Definition {
730
731    /// This function creates a new empty `Definition` for the version provided.
732    pub fn new(version: i32, schema_patches: Option<&DefinitionPatch>) -> Definition {
733        Definition {
734            version,
735            localised_fields: vec![],
736            fields: vec![],
737            localised_key_order: vec![],
738            patches: schema_patches.cloned().unwrap_or_default(),
739        }
740    }
741
742    /// This function creates a new empty `Definition` for the version provided, with the fields provided.
743    pub fn new_with_fields(version: i32, fields: &[Field], loc_fields: &[Field], schema_patches: Option<&DefinitionPatch>) -> Definition {
744        Definition {
745            version,
746            localised_fields: loc_fields.to_vec(),
747            fields: fields.to_vec(),
748            localised_key_order: vec![],
749            patches: schema_patches.cloned().unwrap_or_default(),
750        }
751    }
752
753    /// This function returns the reference and lookup data of a definition.
754    pub fn reference_data(&self) -> BTreeMap<i32, (String, String, Option<Vec<String>>)> {
755        self.fields.iter()
756            .enumerate()
757            .filter(|x| x.1.is_reference.is_some())
758            .map(|x| (x.0 as i32, (x.1.is_reference.clone().unwrap().0, x.1.is_reference.clone().unwrap().1, x.1.lookup.clone())))
759            .collect()
760    }
761
762    /// This function returns the list of fields a table contains, after it has been expanded/changed due to the attributes of each field.
763    pub fn fields_processed(&self) -> Vec<Field> {
764        let mut split_colour_fields: BTreeMap<u8, Field> = BTreeMap::new();
765        let patches = Some(self.patches());
766        let mut fields = self.fields().iter()
767            .filter_map(|x|
768                if x.is_bitwise() > 1 {
769                    let unused = x.unused(patches);
770                    let mut fields = vec![x.clone(); x.is_bitwise() as usize];
771                    fields.iter_mut().enumerate().for_each(|(index, field)| {
772                        field.set_name(format!("{}_{}", field.name(), index + 1));
773                        field.set_field_type(FieldType::Boolean);
774                        field.set_unused(unused);
775                    });
776                    Some(fields)
777                }
778
779                else if !x.enum_values().is_empty() {
780                    let mut field = x.clone();
781                    field.set_field_type(FieldType::StringU8);
782                    Some(vec![field; 1])
783                }
784
785                else if let Some(colour_index) = x.is_part_of_colour() {
786                    match split_colour_fields.get_mut(&colour_index) {
787
788                        // If found, add the default value to the other previously known default value.
789                        Some(field) => {
790                            let default_value = match x.default_value(None) {
791                                Some(default_value) => {
792                                    if x.name.ends_with("_r") || x.name.ends_with("_red") || x.name == "r" || x.name == "red" {
793                                        field.default_value.clone().map(|df| {
794                                            format!("{:X}{}", default_value.parse::<i32>().unwrap_or(0), &df[2..])
795                                        })
796                                    } else if x.name.ends_with("_g") || x.name.ends_with("_green") || x.name == "g" || x.name == "green" {
797                                        field.default_value.clone().map(|df| {
798                                            format!("{}{:X}{}", &df[..2], default_value.parse::<i32>().unwrap_or(0), &df[4..])
799                                        })
800                                    } else if x.name.ends_with("_b") || x.name.ends_with("_blue") || x.name == "b" || x.name == "blue" {
801                                        field.default_value.clone().map(|df| {
802                                            format!("{}{:X}", &df[..4], default_value.parse::<i32>().unwrap_or(0))
803                                        })
804                                    } else {
805                                        Some("000000".to_owned())
806                                    }
807                                }
808                                None => Some("000000".to_owned())
809                            };
810
811                            // Update the default value with the one for this colour.
812                            field.set_default_value(default_value);
813
814                            if !field.unused(patches) {
815                                field.set_unused(x.unused(patches));
816                            }
817                        },
818                        None => {
819                            let unused = x.unused(patches);
820                            let colour_split = x.name().rsplitn(2, '_').collect::<Vec<&str>>();
821                            let colour_field_name = if colour_split.len() == 2 {
822                                format!("{}{}", colour_split[1].to_lowercase(), MERGE_COLOUR_POST)
823                            } else {
824                                format!("{}_{}", MERGE_COLOUR_NO_NAME.to_lowercase(), colour_index)
825                            };
826
827                            let mut field = x.clone();
828                            field.set_name(colour_field_name);
829                            field.set_field_type(FieldType::ColourRGB);
830                            field.set_unused(unused);
831
832                            // We need to fix the default value so it's a ColourRGB one.
833                            let default_value = match field.default_value(None) {
834                                Some(default_value) => {
835                                    if x.name.ends_with("_r") || x.name.ends_with("_red") || x.name == "r" || x.name == "red" {
836                                        Some(format!("{:X}0000", default_value.parse::<i32>().unwrap_or(0)))
837                                    } else if x.name.ends_with("_g") || x.name.ends_with("_green") || x.name == "g" || x.name == "green" {
838                                        Some(format!("00{:X}00", default_value.parse::<i32>().unwrap_or(0)))
839                                    } else if x.name.ends_with("_b") || x.name.ends_with("_blue") || x.name == "b" || x.name == "blue" {
840                                        Some(format!("0000{:X}", default_value.parse::<i32>().unwrap_or(0)))
841                                    } else {
842                                        Some("000000".to_owned())
843                                    }
844                                }
845                                None => Some("000000".to_owned())
846                            };
847
848                            field.set_default_value(default_value);
849
850                            split_colour_fields.insert(colour_index, field);
851                        }
852                    }
853
854                    None
855                }
856
857                else if x.is_numeric(patches) {
858                    let mut field = x.clone();
859                    field.set_field_type(FieldType::I32);
860                    Some(vec![field; 1])
861                }
862
863                else {
864                    Some(vec![x.clone(); 1])
865                }
866            )
867            .flatten()
868            .collect::<Vec<Field>>();
869
870        // Second pass to add the combined colour fields.
871        fields.append(&mut split_colour_fields.values().cloned().collect::<Vec<Field>>());
872        fields
873    }
874
875    /// Note, this doesn't work with combined fields.
876    pub fn original_field_from_processed(&self, index: usize) -> Field {
877        let fields = self.fields();
878        let processed = self.fields_processed();
879
880        let field_processed = &processed[index];
881        let name = if field_processed.is_bitwise() > 1 {
882            let mut name = field_processed.name().to_owned();
883            name.drain(..name.rfind('_').unwrap()).collect::<String>()
884        }
885        else {field_processed.name().to_owned() };
886
887        fields.iter().find(|x| *x.name() == name).unwrap().clone()
888    }
889
890    /// This function returns the field list of a definition, properly sorted.
891    pub fn fields_processed_sorted(&self, key_first: bool) -> Vec<Field> {
892        let mut fields = self.fields_processed();
893        let patches = Some(self.patches());
894        fields.sort_by(|a, b| {
895            if key_first {
896                if a.is_key(patches) && b.is_key(patches) { Ordering::Equal }
897                else if a.is_key(patches) && !b.is_key(patches) { Ordering::Less }
898                else if !a.is_key(patches) && b.is_key(patches) { Ordering::Greater }
899                else { Ordering::Equal }
900            }
901            else if a.ca_order() == -1 || b.ca_order() == -1 { Ordering::Equal }
902            else { a.ca_order().cmp(&b.ca_order()) }
903        });
904        fields
905    }
906
907    /// This function returns the position of a column in a definition, or an error if the column is not found.
908    pub fn column_position_by_name(&self, column_name: &str) -> Option<usize> {
909        self.fields_processed()
910            .iter()
911            .position(|x| x.name() == column_name)
912    }
913
914    /// This function returns the position of all key columns on a table.
915    pub fn key_column_positions(&self) -> Vec<usize> {
916        self.fields_processed()
917            .iter()
918            .enumerate()
919            .filter(|(_, x)| x.is_key(Some(self.patches())))
920            .map(|(x, _)| x)
921            .collect::<Vec<_>>()
922    }
923
924    /// This function returns the position of all key columns on a table, in the same order as they're in the assembly kit.
925    ///
926    /// This is mainly needed for twad_key_deletes, because for some reason the game uses dave's order, not the binary order for the keys.
927    pub fn key_column_positions_by_ca_order(&self) -> Vec<usize> {
928        let fields_processed = self.fields_processed();
929        let mut keys = fields_processed
930            .iter()
931            .enumerate()
932            .filter(|(_, x)| x.is_key(Some(self.patches())))
933            .map(|(x, _)| x)
934            .collect::<Vec<_>>();
935
936        keys.sort_by_key(|x| fields_processed[*x].ca_order);
937        keys
938    }
939
940    /// This function maps a table definition to a `CREATE TABLE` SQL Query.
941    ///
942    /// NOTE: While this function supports creating a table with foreign keys,
943    /// said support is disabled because TW tables are not really foreign key-friendly. Specially in mods.
944    #[cfg(feature = "integration_sqlite")]
945    pub fn map_to_sql_create_table_string(&self, table_name: &str) -> String {
946        let patches = Some(self.patches());
947        let fields_sorted = self.fields_processed();
948        let fields_query = fields_sorted.iter().map(|field| field.map_to_sql_string(patches)).collect::<Vec<_>>().join(",");
949
950        let local_keys_join = fields_sorted.iter().filter_map(|field| if field.is_key(patches) { Some(format!("\"{}\"", field.name()))} else { None }).collect::<Vec<_>>().join(",");
951        let local_keys = format!("CONSTRAINT unique_key PRIMARY KEY (\"pack_name\", \"file_name\", {local_keys_join})");
952        //let foreign_keys = fields_sorted.iter()
953        //    .filter_map(|field| field.is_reference(patches).clone().map(|(ref_table, ref_column)| (field.name(), ref_table, ref_column)))
954        //    .map(|(loc_name, ref_table, ref_field)| format!("CONSTRAINT fk_{table_name} FOREIGN KEY (\"{loc_name}\") REFERENCES {ref_table}(\"{ref_field}\") ON UPDATE CASCADE ON DELETE CASCADE"))
955        //    .collect::<Vec<_>>()
956        //    .join(",");
957
958        //if foreign_keys.is_empty() {
959            if local_keys_join.is_empty() {
960                format!("CREATE TABLE \"{}_v{}\" (\"pack_name\" STRING NOT NULL, \"file_name\" STRING NOT NULL, \"is_vanilla\" INTEGER DEFAULT 0, {})",
961                    table_name.replace('\"', "'"),
962                    self.version(),
963                    fields_query
964                )
965            } else {
966                format!("CREATE TABLE \"{}_v{}\" (\"pack_name\" STRING NOT NULL, \"file_name\" STRING NOT NULL, \"is_vanilla\" INTEGER DEFAULT 0, {}, {})",
967                    table_name.replace('\"', "'"),
968                    self.version(),
969                    fields_query,
970                    local_keys
971                )
972            }
973        /*} else if local_keys_join.is_empty() {
974            format!("CREATE TABLE \"{}_v{}\" (\"table_unique_id\" INTEGER DEFAULT 0, {}, {})",
975                table_name.replace('\"', "'"),
976                self.version(),
977                fields_query,
978                foreign_keys
979            )
980        } else {
981            format!("CREATE TABLE \"{}_v{}\" (\"table_unique_id\" INTEGER DEFAULT 0, {}, {}, {})",
982                table_name.replace('\"', "'"),
983                self.version(),
984                fields_query,
985                local_keys,
986                foreign_keys
987            )
988        }*/
989    }
990
991    /// This function maps a table definition to a `CREATE TABLE` SQL Query.
992    #[cfg(feature = "integration_sqlite")]
993    pub fn map_to_sql_insert_into_string(&self) -> String {
994        let fields_sorted = self.fields_processed();
995        let fields_query = fields_sorted.iter().map(|field| format!("\"{}\"", field.name())).collect::<Vec<_>>().join(",");
996        let fields_query = format!("(\"pack_name\", \"file_name\", \"is_vanilla\", {fields_query})");
997
998        fields_query
999    }
1000
1001    /// This function updates the fields in the provided definition with the data in the provided RawDefinition.
1002    ///
1003    /// Not all data is updated though, only:
1004    /// - Is Key.
1005    /// - Max Length.
1006    /// - Default Value.
1007    /// - Filename Relative Path.
1008    /// - Is Filename.
1009    /// - Is Reference.
1010    /// - Lookup.
1011    /// - CA Order.
1012    #[cfg(feature = "integration_assembly_kit")]
1013    pub fn update_from_raw_definition(&mut self, raw_definition: &RawDefinition, unfound_fields: &mut Vec<String>) {
1014        let raw_table_name = &raw_definition.name.as_ref().unwrap()[..raw_definition.name.as_ref().unwrap().len() - 4];
1015        let mut combined_fields = BTreeMap::new();
1016        for (index, raw_field) in raw_definition.fields.iter().enumerate() {
1017
1018            let mut found = false;
1019            for field in &mut self.fields {
1020                if field.name == raw_field.name {
1021                    if (raw_field.primary_key == "1" && !field.is_key) || (raw_field.primary_key == "0" && field.is_key) {
1022                        field.is_key = raw_field.primary_key == "1";
1023                    }
1024
1025                    if raw_field.default_value.is_some() {
1026                        field.default_value = raw_field.default_value.clone();
1027                    }
1028
1029                    if let Some(ref path) = raw_field.filename_relative_path {
1030                        let mut new_path = path.to_owned();
1031                        if path.contains(",") {
1032                            new_path = path.split(',').map(|x| x.trim()).join(";");
1033                        }
1034
1035                        field.filename_relative_path = Some(new_path);
1036                    }
1037
1038                    // Some fields are marked as filename, but only have fragment paths, which do not seem to correlate to game file paths.
1039                    // We need to disable those to avoid false positives on diagnostics.
1040                    field.is_filename = match raw_field.is_filename {
1041                        Some(_) => !(raw_field.fragment_path.is_some() && raw_field.filename_relative_path.is_none()),
1042                        None => false,
1043                    };
1044
1045                    // Make sure to cleanup any old invalid definition.
1046                    if let Some(ref description) = raw_field.field_description {
1047                        field.description = description.to_owned();
1048                    } else {
1049                        field.description = String::new();
1050                    }
1051
1052                    // We reset these so we don't inherit wrong references from older tables.
1053                    field.is_reference = Default::default();
1054                    field.lookup = Default::default();
1055                    if let Some(ref table) = raw_field.column_source_table {
1056                        if let Some(ref columns) = raw_field.column_source_column {
1057                            if !table.is_empty() && !columns.is_empty() && !columns[0].is_empty() {
1058                                field.is_reference = Some((table.to_owned(), columns[0].to_owned()));
1059                                if columns.len() > 1 {
1060                                    field.lookup = Some(columns[1..].to_vec());
1061                                }
1062                            }
1063                        }
1064                    }
1065
1066                    field.ca_order = index as i16;
1067
1068                    // Detect and group colour fiels.
1069                    let is_numeric = matches!(field.field_type, FieldType::I16 | FieldType::I32 | FieldType::I64 | FieldType::F32 | FieldType::F64);
1070
1071                    if is_numeric && (
1072                        field.name.ends_with("_r") ||
1073                        field.name.ends_with("_g") ||
1074                        field.name.ends_with("_b") ||
1075                        field.name.ends_with("_red") ||
1076                        field.name.ends_with("_green") ||
1077                        field.name.ends_with("_blue") ||
1078                        field.name == "r" ||
1079                        field.name == "g" ||
1080                        field.name == "b" ||
1081                        field.name == "red" ||
1082                        field.name == "green" ||
1083                        field.name == "blue"
1084                    ) {
1085                        let colour_split = field.name.rsplitn(2, '_').collect::<Vec<&str>>();
1086                        let colour_field_name = if colour_split.len() == 2 { format!("{}{}", colour_split[1].to_lowercase(), MERGE_COLOUR_POST) } else { MERGE_COLOUR_NO_NAME.to_lowercase() };
1087
1088                        match combined_fields.get(&colour_field_name) {
1089                            Some(group_key) => field.is_part_of_colour = Some(*group_key),
1090                            None => {
1091                                let group_key = combined_fields.keys().len() as u8 + 1;
1092                                combined_fields.insert(colour_field_name.to_owned(), group_key);
1093                                field.is_part_of_colour = Some(group_key);
1094                            }
1095                        }
1096                    }
1097                    found = true;
1098                    break;
1099                }
1100            }
1101
1102            if !found {
1103
1104                // We need to check if it's a loc field before reporting it as unfound.
1105                for loc_field in self.localised_fields() {
1106                    if loc_field.name == raw_field.name {
1107                        found = true;
1108                        break;
1109                    }
1110                }
1111
1112                // We automatically ignore certain old fields that have nothing to do with the game's data.
1113                if !found && !IGNORABLE_FIELDS.contains(&&*raw_field.name) {
1114                    unfound_fields.push(format!("{}/{}", raw_table_name, raw_field.name));
1115                }
1116            }
1117        }
1118    }
1119
1120    /// This function populates the `localised_fields` of a definition with data from the assembly kit.
1121    #[cfg(feature = "integration_assembly_kit")]
1122    pub fn update_from_raw_localisable_fields(&mut self, raw_definition: &RawDefinition, raw_localisable_fields: &[RawLocalisableField]) {
1123        let raw_table_name = &raw_definition.name.as_ref().unwrap()[..raw_definition.name.as_ref().unwrap().len() - 4];
1124        let localisable_fields_names = raw_localisable_fields.iter()
1125            .filter(|x| x.table_name == raw_table_name)
1126            .map(|x| &*x.field)
1127            .collect::<Vec<&str>>();
1128
1129        if !localisable_fields_names.is_empty() {
1130            let localisable_fields = raw_definition.fields.iter()
1131                .filter(|x| localisable_fields_names.contains(&&*x.name))
1132                .collect::<Vec<&RawField>>();
1133
1134            self.localised_fields = localisable_fields.iter().map(|x| From::from(*x)).collect();
1135
1136            // Set their type to StringU8 for consistency.
1137            self.localised_fields.iter_mut().for_each(|field| field.field_type = FieldType::StringU8);
1138        }
1139    }
1140}
1141
1142/// Implementation of `Field`.
1143impl Field {
1144
1145    /// This function creates a `Field` using the provided data.
1146    pub fn new(
1147        name: String,
1148        field_type: FieldType,
1149        is_key: bool,
1150        default_value: Option<String>,
1151        is_filename: bool,
1152        filename_relative_path: Option<String>,
1153        is_reference: Option<(String, String)>,
1154        lookup: Option<Vec<String>>,
1155        description: String,
1156        ca_order: i16,
1157        is_bitwise: i32,
1158        enum_values: BTreeMap<i32, String>,
1159        is_part_of_colour: Option<u8>,
1160    ) -> Self {
1161        Self {
1162            name,
1163            field_type,
1164            is_key,
1165            default_value,
1166            is_filename,
1167            filename_relative_path,
1168            is_reference,
1169            lookup,
1170            description,
1171            ca_order,
1172            is_bitwise,
1173            enum_values,
1174            is_part_of_colour,
1175            unused: false
1176        }
1177    }
1178
1179    //----------------------------------------------------------------------//
1180    // Manual getter implementations, because we need to tweak some of them.
1181    //----------------------------------------------------------------------//
1182    pub fn name(&self) -> &str {
1183        &self.name
1184    }
1185    pub fn field_type(&self) -> &FieldType {
1186        &self.field_type
1187    }
1188    pub fn is_key(&self, schema_patches: Option<&DefinitionPatch>) -> bool {
1189        if let Some(schema_patches) = schema_patches {
1190            if let Some(patch) = schema_patches.get(self.name()) {
1191                if let Some(field_patch) = patch.get("is_key") {
1192                    return field_patch.parse().unwrap_or(false);
1193                }
1194            }
1195        }
1196
1197        self.is_key
1198    }
1199
1200    pub fn default_value(&self, schema_patches: Option<&DefinitionPatch>) -> Option<String> {
1201        if let Some(schema_patches) = schema_patches {
1202            if let Some(patch) = schema_patches.get(self.name()) {
1203                if let Some(field_patch) = patch.get("default_value") {
1204                    return Some(field_patch.to_string());
1205                }
1206            }
1207        }
1208
1209        self.default_value.clone()
1210    }
1211
1212    pub fn is_filename(&self, schema_patches: Option<&DefinitionPatch>) -> bool {
1213        if let Some(schema_patches) = schema_patches {
1214            if let Some(patch) = schema_patches.get(self.name()) {
1215                if let Some(field_patch) = patch.get("is_filename") {
1216                    return field_patch.parse().unwrap_or(false);
1217                }
1218            }
1219        }
1220
1221        self.is_filename
1222    }
1223
1224    pub fn filename_relative_path(&self, schema_patches: Option<&DefinitionPatch>) -> Option<Vec<String>> {
1225        if let Some(schema_patches) = schema_patches {
1226            if let Some(patch) = schema_patches.get(self.name()) {
1227                if let Some(field_patch) = patch.get("filename_relative_path") {
1228                    return Some(field_patch.replace('\\', "/").split(';').map(|x| x.to_string()).collect::<Vec<String>>());
1229                }
1230            }
1231        }
1232
1233        self.filename_relative_path.clone().map(|x| x.replace('\\', "/").split(';').map(|x| x.to_string()).collect::<Vec<String>>())
1234    }
1235
1236    pub fn is_reference(&self, schema_patches: Option<&DefinitionPatch>) -> Option<(String,String)> {
1237        if let Some(schema_patches) = schema_patches {
1238            if let Some(patch) = schema_patches.get(self.name()) {
1239                if let Some(field_patch) = patch.get("is_reference") {
1240                    let split = field_patch.splitn(2, ';').collect::<Vec<_>>();
1241                    if split.len() == 2 {
1242                        return Some((split[0].to_string(), split[1].to_string()));
1243                    }
1244                }
1245            }
1246        }
1247
1248        self.is_reference.clone()
1249    }
1250
1251    pub fn lookup(&self, schema_patches: Option<&DefinitionPatch>) -> Option<Vec<String>> {
1252        if let Some(schema_patches) = schema_patches {
1253            if let Some(patch) = schema_patches.get(self.name()) {
1254                if let Some(field_patch) = patch.get("lookup") {
1255                    return Some(field_patch.split(';').map(|x| x.to_string()).collect());
1256                }
1257            }
1258        }
1259
1260        self.lookup.clone()
1261    }
1262
1263    pub fn lookup_no_patch(&self) -> Option<Vec<String>> {
1264        self.lookup.clone()
1265    }
1266
1267    pub fn lookup_hardcoded(&self, schema_patches: Option<&DefinitionPatch>) -> HashMap<String, String> {
1268        if let Some(schema_patches) = schema_patches {
1269            if let Some(patch) = schema_patches.get(self.name()) {
1270                if let Some(field_patch) = patch.get("lookup_hardcoded") {
1271                    let entries = field_patch.split(":::::").map(|x| x.split(";;;;;").collect::<Vec<_>>()).collect::<Vec<_>>();
1272                    let mut hashmap = HashMap::new();
1273                    for entry in entries {
1274                        hashmap.insert(entry[0].to_owned(), entry[1].to_owned());
1275                    }
1276                    return hashmap;
1277                }
1278            }
1279        }
1280
1281        HashMap::new()
1282    }
1283
1284    pub fn description(&self, schema_patches: Option<&DefinitionPatch>) -> String {
1285        if let Some(schema_patches) = schema_patches {
1286            if let Some(patch) = schema_patches.get(self.name()) {
1287                if let Some(field_patch) = patch.get("description") {
1288                    return field_patch.to_owned();
1289                }
1290            }
1291        }
1292
1293        self.description.to_owned()
1294    }
1295
1296    pub fn ca_order(&self) ->  i16 {
1297        self.ca_order
1298    }
1299    pub fn is_bitwise(&self) -> i32 {
1300        self.is_bitwise
1301    }
1302    pub fn enum_values(&self) -> &BTreeMap<i32,String> {
1303        &self.enum_values
1304    }
1305
1306    /// Getter for the `enum_values` field, in an option.
1307    pub fn enum_values_to_option(&self) -> Option<BTreeMap<i32, String>> {
1308        if self.enum_values.is_empty() { None }
1309        else { Some(self.enum_values.clone()) }
1310    }
1311
1312    /// Getter for the `enum_values` field in a string format.
1313    pub fn enum_values_to_string(&self) -> String {
1314        self.enum_values.iter().map(|(x, y)| format!("{x},{y}")).collect::<Vec<String>>().join(";")
1315    }
1316
1317    pub fn is_part_of_colour(&self) -> Option<u8>{
1318        self.is_part_of_colour
1319    }
1320
1321    pub fn is_numeric(&self, _schema_patches: Option<&DefinitionPatch>) -> bool {
1322        return false;
1323        /*
1324        if let Some(schema_patches) = schema_patches {
1325            if let Some(patch) = schema_patches.get(self.name()) {
1326                if let Some(is_numeric) = patch.get("is_numeric") {
1327                    return is_numeric.parse::<bool>().unwrap_or(false);
1328                }
1329            }
1330        }
1331
1332        false*/
1333    }
1334
1335    /// Getter for the `cannot_be_empty` field.
1336    pub fn cannot_be_empty(&self, schema_patches: Option<&DefinitionPatch>) -> bool {
1337        if let Some(schema_patches) = schema_patches {
1338            if let Some(patch) = schema_patches.get(self.name()) {
1339                if let Some(cannot_be_empty) = patch.get("not_empty") {
1340                    return cannot_be_empty.parse::<bool>().unwrap_or(false);
1341                }
1342            }
1343        }
1344
1345        false
1346    }
1347
1348    /// Getter for the `unused` field.
1349    pub fn unused(&self, schema_patches: Option<&DefinitionPatch>) -> bool {
1350
1351        // By default all fields are used, except the ones set through patches. If it's unused, skip patches.
1352        self.unused || {
1353
1354            if let Some(schema_patches) = schema_patches {
1355                if let Some(patch) = schema_patches.get(self.name()) {
1356                    if let Some(cannot_be_empty) = patch.get("unused") {
1357                        return cannot_be_empty.parse::<bool>().unwrap_or(false);
1358                    }
1359                }
1360            }
1361
1362            false
1363        }
1364    }
1365
1366    /// This function maps our field to a String ready to be used in a SQL `CREATE TABLE` command.
1367    #[cfg(feature = "integration_sqlite")]
1368    pub fn map_to_sql_string(&self, schema_patches: Option<&DefinitionPatch>) -> String {
1369        let mut string = format!(" \"{}\" {:?} ", self.name(), self.field_type().map_to_sql_type());
1370
1371        if let Some(default_value) = self.default_value(schema_patches) {
1372            string.push_str(&format!(" DEFAULT \"{}\"", default_value.replace("\"", "\"\"")));
1373        }
1374
1375        string
1376    }
1377}
1378
1379impl FieldType {
1380
1381    /// This function maps our type to a SQLite Type.
1382    #[cfg(feature = "integration_sqlite")]
1383    pub fn map_to_sql_type(&self) -> Type {
1384        match self {
1385            FieldType::Boolean => Type::Integer,
1386            FieldType::F32 => Type::Real,
1387            FieldType::F64 => Type::Real,
1388            FieldType::I16 => Type::Integer,
1389            FieldType::I32 => Type::Integer,
1390            FieldType::I64 => Type::Integer,
1391            FieldType::ColourRGB => Type::Text,
1392            FieldType::StringU8 => Type::Text,
1393            FieldType::StringU16 => Type::Text,
1394            FieldType::OptionalI16 => Type::Integer,
1395            FieldType::OptionalI32 => Type::Integer,
1396            FieldType::OptionalI64 => Type::Integer,
1397            FieldType::OptionalStringU8 => Type::Text,
1398            FieldType::OptionalStringU16 => Type::Text,
1399            FieldType::SequenceU16(_) => Type::Blob,
1400            FieldType::SequenceU32(_) => Type::Blob,
1401        }
1402    }
1403}
1404//---------------------------------------------------------------------------//
1405//                         Extra Implementations
1406//---------------------------------------------------------------------------//
1407
1408/// Default implementation of `Schema`.
1409impl Default for Schema {
1410    fn default() -> Self {
1411        Self {
1412            version: CURRENT_STRUCTURAL_VERSION,
1413            definitions: HashMap::new(),
1414            patches: HashMap::new()
1415        }
1416    }
1417}
1418
1419/// Default implementation of `FieldType`.
1420impl Default for Field {
1421    fn default() -> Self {
1422        Self {
1423            name: String::from("new_field"),
1424            field_type: FieldType::StringU8,
1425            is_key: false,
1426            default_value: None,
1427            is_filename: false,
1428            filename_relative_path: None,
1429            is_reference: None,
1430            lookup: None,
1431            description: String::from(""),
1432            ca_order: -1,
1433            is_bitwise: 0,
1434            enum_values: BTreeMap::new(),
1435            is_part_of_colour: None,
1436            unused: false,
1437        }
1438    }
1439}
1440
1441/// Display implementation of `FieldType`.
1442impl Display for FieldType {
1443    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1444        match self {
1445            FieldType::Boolean => write!(f, "Boolean"),
1446            FieldType::F32 => write!(f, "F32"),
1447            FieldType::F64 => write!(f, "F64"),
1448            FieldType::I16 => write!(f, "I16"),
1449            FieldType::I32 => write!(f, "I32"),
1450            FieldType::I64 => write!(f, "I64"),
1451            FieldType::ColourRGB => write!(f, "ColourRGB"),
1452            FieldType::StringU8 => write!(f, "StringU8"),
1453            FieldType::StringU16 => write!(f, "StringU16"),
1454            FieldType::OptionalI16 => write!(f, "OptionalI16"),
1455            FieldType::OptionalI32 => write!(f, "OptionalI32"),
1456            FieldType::OptionalI64 => write!(f, "OptionalI64"),
1457            FieldType::OptionalStringU8 => write!(f, "OptionalStringU8"),
1458            FieldType::OptionalStringU16 => write!(f, "OptionalStringU16"),
1459            FieldType::SequenceU16(_) => write!(f, "SequenceU16"),
1460            FieldType::SequenceU32(_) => write!(f, "SequenceU32"),
1461        }
1462    }
1463}
1464
1465/// Implementation of `From<&RawDefinition>` for `Definition.
1466impl From<&DecodedData> for FieldType {
1467    fn from(data: &DecodedData) -> Self {
1468        match data {
1469            DecodedData::Boolean(_) => FieldType::Boolean,
1470            DecodedData::F32(_) => FieldType::F32,
1471            DecodedData::F64(_) => FieldType::F64,
1472            DecodedData::I16(_) => FieldType::I16,
1473            DecodedData::I32(_) => FieldType::I32,
1474            DecodedData::I64(_) => FieldType::I64,
1475            DecodedData::ColourRGB(_) => FieldType::ColourRGB,
1476            DecodedData::StringU8(_) => FieldType::StringU8,
1477            DecodedData::StringU16(_) => FieldType::StringU16,
1478            DecodedData::OptionalI16(_) => FieldType::OptionalI16,
1479            DecodedData::OptionalI32(_) => FieldType::OptionalI32,
1480            DecodedData::OptionalI64(_) => FieldType::OptionalI64,
1481            DecodedData::OptionalStringU8(_) => FieldType::OptionalStringU8,
1482            DecodedData::OptionalStringU16(_) => FieldType::OptionalStringU16,
1483            DecodedData::SequenceU16(_) => FieldType::SequenceU16(Box::new(Definition::new(INVALID_VERSION, None))),
1484            DecodedData::SequenceU32(_) => FieldType::SequenceU32(Box::new(Definition::new(INVALID_VERSION, None))),
1485        }
1486    }
1487}
1488
1489/// Special serializer function to sort the definitions HashMap before serializing.
1490fn ordered_map_definitions<S>(value: &HashMap<String, Vec<Definition>>, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer, {
1491    let ordered: BTreeMap<_, _> = value.iter().collect();
1492    ordered.serialize(serializer)
1493}
1494
1495/// Special serializer function to sort the patches HashMap before serializing.
1496fn ordered_map_patches<S>(value: &HashMap<String, HashMap<String, HashMap<String, String>>>, serializer: S) -> Result<S::Ok, S::Error> where S: Serializer, {
1497    let ordered: BTreeMap<_, BTreeMap<_, BTreeMap<_, _>>> = value.iter().map(|(a, x)| (a, x.iter().map(|(b, y)| (b, y.iter().collect())).collect())).collect();
1498    ordered.serialize(serializer)
1499}