1use 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
91pub(crate) mod v4;
93
94pub const SCHEMA_FOLDER: &str = "schemas";
96
97pub const SCHEMA_REPO: &str = "https://github.com/Frodo45127/rpfm-schemas";
100pub const SCHEMA_REMOTE: &str = "origin";
101pub const SCHEMA_BRANCH: &str = "master";
102
103const CURRENT_STRUCTURAL_VERSION: u16 = 5;
105const INVALID_VERSION: i32 = -100;
106
107pub const MERGE_COLOUR_NO_NAME: &str = "Unnamed Colour Group";
109
110pub const MERGE_COLOUR_POST: &str = "_hex";
112
113const IGNORABLE_FIELDS: [&str; 4] = ["s_ColLineage", "s_Generation", "s_GUID", "s_Lineage"];
115
116pub type DefinitionPatch = HashMap<String, HashMap<String, String>>;
124
125#[derive(Clone, PartialEq, Eq, Debug, Getters, MutGetters, Setters, Serialize, Deserialize)]
127#[getset(get = "pub", get_mut = "pub", set = "pub")]
128pub struct Schema {
129
130 version: u16,
132
133 #[serde(serialize_with = "ordered_map_definitions")]
135 definitions: HashMap<String, Vec<Definition>>,
136
137 #[serde(serialize_with = "ordered_map_patches")]
139 patches: HashMap<String, DefinitionPatch>,
140}
141
142#[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 version: i32,
152
153 fields: Vec<Field>,
155
156 localised_fields: Vec<Field>,
158
159 localised_key_order: Vec<u32>,
163
164 #[serde(skip)]
168 patches: DefinitionPatch
169}
170
171#[derive(Clone, PartialEq, Eq, Debug, Setters, Serialize, Deserialize)]
173#[getset(set = "pub")]
174pub struct Field {
175
176 name: String,
178
179 field_type: FieldType,
181
182 is_key: bool,
184
185 default_value: Option<String>,
187
188 is_filename: bool,
190
191 filename_relative_path: Option<String>,
193
194 is_reference: Option<(String, String)>,
196
197 lookup: Option<Vec<String>>,
199
200 description: String,
202
203 ca_order: i16,
205
206 is_bitwise: i32,
208
209 enum_values: BTreeMap<i32, String>,
211
212 is_part_of_colour: Option<u8>,
214
215 #[serde(skip_serializing, skip_deserializing)]
217 unused: bool,
218}
219
220#[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
241impl Schema {
247
248 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 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 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 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 pub fn patches_for_table(&self, table_name: &str) -> Option<&DefinitionPatch> {
305 self.patches.get(table_name)
306 }
307
308 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 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 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 pub fn definitions_by_table_name_cloned(&self, table_name: &str) -> Option<Vec<Definition>> {
360 self.definitions.get(table_name).cloned()
361 }
362
363 pub fn definitions_by_table_name(&self, table_name: &str) -> Option<&Vec<Definition>> {
365 self.definitions.get(table_name)
366 }
367
368 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 pub fn definition_newer(&self, table_name: &str, candidates: &[Definition]) -> Option<&Definition> {
378
379 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 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 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 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 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 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 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 pub fn save(&mut self, path: &Path) -> Result<()> {
456
457 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 self.definitions.iter_mut().for_each(|(table_name, definitions)| {
469 definitions.sort_by(|a, b| b.version().cmp(a.version()));
470
471 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 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 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 pub fn save_json(&mut self, path: &Path) -> Result<()> {
511 let mut path = path.to_path_buf();
512 path.set_extension("json");
513
514 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 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 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 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 pub fn referencing_columns_for_table(&self, table_name: &str, definition: &Definition) -> HashMap<String, HashMap<String, Vec<String>>> {
575
576 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 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 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 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 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 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 if !columns.is_empty() {
664 tables.insert(ref_table_name.to_owned(), columns);
665 }
666 }
667
668 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 pub fn load_patches_from_str(patch: &str) -> Result<HashMap<String, DefinitionPatch>> {
678 from_str(patch).map_err(From::from)
679 }
680
681 pub fn load_definitions_from_str(definition: &str) -> Result<HashMap<String, Definition>> {
683 from_str(definition).map_err(From::from)
684 }
685
686 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 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 #[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 #[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 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 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 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 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 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 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 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 fields.append(&mut split_colour_fields.values().cloned().collect::<Vec<Field>>());
872 fields
873 }
874
875 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 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 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 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 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 #[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 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 }
990
991 #[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 #[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 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 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 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 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 for loc_field in self.localised_fields() {
1106 if loc_field.name == raw_field.name {
1107 found = true;
1108 break;
1109 }
1110 }
1111
1112 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 #[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 self.localised_fields.iter_mut().for_each(|field| field.field_type = FieldType::StringU8);
1138 }
1139 }
1140}
1141
1142impl Field {
1144
1145 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 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 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 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 }
1334
1335 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 pub fn unused(&self, schema_patches: Option<&DefinitionPatch>) -> bool {
1350
1351 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 #[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 #[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}
1404impl 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
1419impl 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
1441impl 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
1465impl 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
1489fn 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
1495fn 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}