1use anyhow::Result;
14use itertools::Itertools;
15use rayon::prelude::*;
16
17use std::collections::HashMap;
18
19use rpfm_ipc::api::ApiError;
20use rpfm_ipc::api::schema::{DeleteDefinition, GetReferencingColumns, GetTablePatches, ImportPatches, PATCH_KEYS, PatchColumn, RawDefinitions, ReferencingColumns, RemovePatches, SchemaTables, SetDefinition, TablePatches};
21use rpfm_ipc::api::tables::{GetTableDefinition, TableDefinition};
22
23use rpfm_lib::files::{Container, db::DB, FileType, RFileDecoded};
24use rpfm_lib::integrations::assembly_kit::update_schema_from_raw_files;
25use rpfm_lib::schema::{Definition, DefinitionPatch, Schema};
26
27use rpfm_ipc::settings::{schemas_path, table_patches_path, Settings};
28
29use super::{SessionState, load_schema, loaded_schema, loaded_schema_mut};
30
31impl SessionState {
32
33 pub fn update_schema_from_asskit(&mut self, settings: &Settings, ignore_game_files_in_ak: bool, disable_uuid_regeneration: bool) -> Result<()> {
41 let schema = loaded_schema_mut(&mut self.schema)?;
42 let asskit_path = settings.assembly_kit_path(&self.game)?;
43 let schema_path = schemas_path()?.join(self.game.schema_file_name());
44
45 let mut tables_to_check = self.dependencies.db_and_loc_data(true, false, true, false)?;
47 for pack in self.packs.values() {
48 if !pack.disk_file_path().is_empty() {
49 tables_to_check.append(&mut pack.files_by_type(&[FileType::DB]));
50 }
51 }
52
53 let mut tables_to_check_split: HashMap<String, Vec<DB>> = HashMap::new();
55 for table_to_check in tables_to_check {
56 if let Ok(RFileDecoded::DB(table)) = table_to_check.decoded() {
57 let tables = tables_to_check_split.entry(table.table_name().to_owned()).or_default();
58 match tables.iter_mut().find(|source| source.definition().version() == table.definition().version()) {
59 Some(db_source) => *db_source = DB::merge(&[db_source, table])?,
60 None => tables.push(table.clone()),
61 }
62 }
63 }
64
65 let tables_to_skip = if ignore_game_files_in_ak {
66 self.dependencies.vanilla_loose_tables().keys().chain(self.dependencies.vanilla_tables().keys()).map(|name| &**name).collect::<Vec<_>>()
67 } else {
68 vec![]
69 };
70
71 let possible_loc_fields = update_schema_from_raw_files(schema, &self.game, &asskit_path, &schema_path, &tables_to_skip, &tables_to_check_split)?;
72
73 let local_packs = if self.packs.is_empty() { None } else { Some(&self.packs) };
76 if self.dependencies.bruteforce_loc_key_order(schema, possible_loc_fields, local_packs, None).is_err() {
77 return Ok(());
78 }
79
80 let _ = update_schema_from_raw_files(schema, &self.game, &asskit_path, &schema_path, &tables_to_skip, &tables_to_check_split);
81
82 if self.dependencies.generate_automatic_patches(schema, &self.packs).is_err() {
84 return Ok(());
85 }
86
87 schema.definitions_mut().par_iter_mut().for_each(|(_, definitions)| {
89 definitions.iter_mut().for_each(|definition| {
90 definition.edit_fields(|fields| fields.iter_mut().for_each(|field| {
91 if let Some(path) = field.filename_relative_path(None) {
92 if path.len() == 1 && path[0].contains(',') {
93 let new_paths = path[0].split(',').map(|path| path.trim()).join(";");
94 field.set_filename_relative_path(Some(new_paths));
95 }
96 }
97 }));
98 });
99 });
100
101 schema.save(&schema_path)?;
102
103 load_schema(&mut self.schema, &mut self.packs, &self.game, disable_uuid_regeneration);
105 Ok(())
106 }
107
108 pub fn reload_schema(&mut self, disable_uuid_regeneration: bool) {
114 load_schema(&mut self.schema, &mut self.packs, &self.game, disable_uuid_regeneration);
115 }
116
117 pub fn save_schema(&mut self, mut schema: Schema) -> Result<()> {
119 schema.save(&schemas_path()?.join(self.game.schema_file_name()))?;
120 self.schema = Some(schema);
121 Ok(())
122 }
123
124 pub fn save_local_schema_patches(&self, patches: &HashMap<String, DefinitionPatch>) -> Result<()> {
126 Schema::save_patches(patches, &table_patches_path()?.join(self.game.schema_file_name()))?;
127 Ok(())
128 }
129
130 pub fn remove_local_schema_patches_for_table(&self, table_name: &str) -> Result<()> {
132 Schema::remove_patches_for_table(table_name, &table_patches_path()?.join(self.game.schema_file_name()))?;
133 Ok(())
134 }
135
136 pub fn remove_local_schema_patches_for_table_and_field(&self, table_name: &str, field_name: &str) -> Result<()> {
138 Schema::remove_patches_for_table_and_field(table_name, field_name, &table_patches_path()?.join(self.game.schema_file_name()))?;
139 Ok(())
140 }
141
142 pub fn import_schema_patches(&mut self, patches: &HashMap<String, DefinitionPatch>) -> Result<()> {
144 let schema = loaded_schema_mut(&mut self.schema)?;
145 Schema::add_patches_to_patch_set(schema.patches_mut(), patches);
146 schema.save(&schemas_path()?.join(self.game.schema_file_name()))?;
147 Ok(())
148 }
149
150 pub fn custom_table_names(&self) -> Result<Vec<String>> {
152 Ok(loaded_schema(&self.schema)?.definitions().par_iter()
153 .filter(|(key, definitions)| !definitions.is_empty() && (key.starts_with("start_pos_") || key.starts_with("twad_")))
154 .map(|(key, _)| key.to_owned())
155 .collect())
156 }
157
158 pub fn definition_by_name_and_version(&self, table_name: &str, version: i32) -> Result<&Definition> {
173 loaded_schema(&self.schema)?.definition_by_name_and_version(table_name, version)
174 .or_else(|| self.dependencies.asskit_only_db_tables().get(table_name)
175 .map(|table| table.definition())
176 .filter(|definition| *definition.version() == version))
177 .ok_or_else(|| ApiError::DefinitionNotFound(table_name.to_owned()).into())
178 }
179
180 pub fn table_definition(&self, request: &GetTableDefinition) -> Result<TableDefinition> {
184 let schema = loaded_schema(&self.schema)?;
185 let table_name = &request.table_name;
186 let definition = match request.version {
187 Some(version) => self.definition_by_name_and_version(table_name, version)?,
188 None => self.dependencies.db_version(table_name)
189 .and_then(|version| schema.definition_by_name_and_version(table_name, version))
190 .or_else(|| schema.definitions_by_table_name(table_name).and_then(|definitions| definitions.first()))
191 .ok_or_else(|| ApiError::DefinitionNotFound(table_name.to_owned()))?,
192 };
193
194 Ok(TableDefinition {
195 table_name: table_name.to_owned(),
196 version: *definition.version(),
197 columns: definition.fields_processed().to_vec(),
198 })
199 }
200
201 pub fn schema_tables(&self, prefix: &str) -> Result<SchemaTables> {
203 let tables = loaded_schema(&self.schema)?.definitions().iter()
204 .filter(|(table_name, definitions)| table_name.starts_with(prefix) && !definitions.is_empty())
205 .map(|(table_name, definitions)| (table_name.to_owned(), definitions.iter().map(|definition| *definition.version()).collect()))
206 .collect();
207
208 Ok(SchemaTables { tables })
209 }
210
211 pub fn patch_column(&mut self, request: &PatchColumn, disable_uuid_regeneration: bool) -> Result<()> {
222 if let Some(key) = request.patch.keys().find(|key| !PATCH_KEYS.iter().any(|(valid_key, _)| valid_key == key)) {
223 let valid_keys = PATCH_KEYS.iter().map(|(key, _)| *key).collect::<Vec<_>>().join(", ");
224 return Err(ApiError::InvalidParams(format!("Unknown patch key: {key}. Valid ones: {valid_keys}.")).into());
225 }
226
227 let column_patch = request.patch.iter().map(|(key, value)| (key.clone(), value.clone())).collect();
228 let patches = HashMap::from([(request.table_name.clone(), HashMap::from([(request.column.clone(), column_patch)]))]);
229 self.save_local_schema_patches(&patches)?;
230 self.reload_schema(disable_uuid_regeneration);
231 Ok(())
232 }
233
234 pub fn remove_patches(&mut self, request: &RemovePatches, disable_uuid_regeneration: bool) -> Result<()> {
241 match request.column {
242 Some(ref column) => self.remove_local_schema_patches_for_table_and_field(&request.table_name, column)?,
243 None => self.remove_local_schema_patches_for_table(&request.table_name)?,
244 }
245
246 self.reload_schema(disable_uuid_regeneration);
247 Ok(())
248 }
249
250 pub fn raw_definitions(&self, table_name: &str, version: Option<i32>) -> Result<RawDefinitions> {
252 let definitions = self.definitions_by_table_name(table_name)?.iter()
253 .filter(|definition| version.is_none_or(|version| *definition.version() == version))
254 .map(|definition| serde_json::to_value(definition.stored()))
255 .collect::<Result<Vec<_>, _>>()?;
256
257 Ok(RawDefinitions { definitions })
258 }
259
260 pub fn set_definition(&mut self, request: &SetDefinition, disable_uuid_regeneration: bool) -> Result<()> {
267 let definition = serde_json::from_value::<Definition>(request.definition.clone())
268 .map_err(|error| ApiError::InvalidParams(format!("Invalid definition: {error}")))?;
269
270 let mut schema = loaded_schema(&self.schema)?.clone();
271 schema.add_definition(&request.table_name, &definition);
272 self.save_schema(schema)?;
273 self.reload_schema(disable_uuid_regeneration);
274 Ok(())
275 }
276
277 pub fn delete_definition_and_save(&mut self, request: &DeleteDefinition, disable_uuid_regeneration: bool) -> Result<()> {
284 let mut schema = loaded_schema(&self.schema)?.clone();
285 schema.remove_definition(&request.table_name, request.version);
286 self.save_schema(schema)?;
287 self.reload_schema(disable_uuid_regeneration);
288 Ok(())
289 }
290
291 pub fn referencing_columns_of(&self, request: &GetReferencingColumns) -> Result<ReferencingColumns> {
293 let table_name = &request.table_name;
294 let version = match request.version {
295 Some(version) => version,
296 None => self.table_definition(&GetTableDefinition { table_name: table_name.to_owned(), version: None })?.version,
297 };
298
299 let definition = self.definition_by_name_and_version(table_name, version)?;
300 let columns = self.referencing_columns(table_name, definition)?.into_iter()
301 .map(|(column, tables)| (column, tables.into_iter().collect()))
302 .collect();
303
304 Ok(ReferencingColumns { columns })
305 }
306
307 pub fn import_patches(&mut self, request: &ImportPatches, disable_uuid_regeneration: bool) -> Result<()> {
314 let patches = request.patches.iter()
315 .map(|(table, columns)| (table.clone(), columns.iter()
316 .map(|(column, patch)| (column.clone(), patch.iter().map(|(key, value)| (key.clone(), value.clone())).collect()))
317 .collect()))
318 .collect::<HashMap<String, DefinitionPatch>>();
319
320 self.import_schema_patches(&patches)?;
321 self.reload_schema(disable_uuid_regeneration);
322 Ok(())
323 }
324
325 pub fn definitions_by_table_name(&self, table_name: &str) -> Result<Vec<Definition>> {
327 Ok(loaded_schema(&self.schema)?.definitions_by_table_name(table_name)
328 .map(|definitions| definitions.to_vec())
329 .unwrap_or_default())
330 }
331
332 pub fn definition_patches(&self, table_name: &str, version: i32) -> Result<DefinitionPatch> {
334 let schema = loaded_schema(&self.schema)?;
335 Ok(match schema.definition_by_name_and_version(table_name, version) {
336 Some(definition) => definition.patches().clone(),
337 None => schema.patches().get(table_name).cloned().unwrap_or_default(),
338 })
339 }
340
341 pub fn table_patches(&self, request: &GetTablePatches) -> Result<TablePatches> {
343 let patches = self.definition_patches(&request.table_name, request.version)?.into_iter()
344 .map(|(column, patch)| (column, patch.into_iter().collect()))
345 .collect();
346
347 Ok(TablePatches { patches })
348 }
349
350 pub fn referencing_columns(&self, table_name: &str, definition: &Definition) -> Result<HashMap<String, HashMap<String, Vec<String>>>> {
352 Ok(loaded_schema(&self.schema)?.referencing_columns_for_table(table_name, definition))
353 }
354}