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 table_definition(&self, request: &GetTableDefinition) -> Result<TableDefinition> {
162 let schema = loaded_schema(&self.schema)?;
163 let table_name = &request.table_name;
164 let definition = match request.version {
165 Some(version) => schema.definition_by_name_and_version(table_name, version),
166 None => self.dependencies.db_version(table_name)
167 .and_then(|version| schema.definition_by_name_and_version(table_name, version))
168 .or_else(|| schema.definitions_by_table_name(table_name).and_then(|definitions| definitions.first())),
169 }.ok_or_else(|| ApiError::DefinitionNotFound(table_name.to_owned()))?;
170
171 Ok(TableDefinition {
172 table_name: table_name.to_owned(),
173 version: *definition.version(),
174 columns: definition.fields_processed().to_vec(),
175 })
176 }
177
178 pub fn schema_tables(&self, prefix: &str) -> Result<SchemaTables> {
180 let tables = loaded_schema(&self.schema)?.definitions().iter()
181 .filter(|(table_name, definitions)| table_name.starts_with(prefix) && !definitions.is_empty())
182 .map(|(table_name, definitions)| (table_name.to_owned(), definitions.iter().map(|definition| *definition.version()).collect()))
183 .collect();
184
185 Ok(SchemaTables { tables })
186 }
187
188 pub fn patch_column(&mut self, request: &PatchColumn, disable_uuid_regeneration: bool) -> Result<()> {
199 if let Some(key) = request.patch.keys().find(|key| !PATCH_KEYS.iter().any(|(valid_key, _)| valid_key == key)) {
200 let valid_keys = PATCH_KEYS.iter().map(|(key, _)| *key).collect::<Vec<_>>().join(", ");
201 return Err(ApiError::InvalidParams(format!("Unknown patch key: {key}. Valid ones: {valid_keys}.")).into());
202 }
203
204 let column_patch = request.patch.iter().map(|(key, value)| (key.clone(), value.clone())).collect();
205 let patches = HashMap::from([(request.table_name.clone(), HashMap::from([(request.column.clone(), column_patch)]))]);
206 self.save_local_schema_patches(&patches)?;
207 self.reload_schema(disable_uuid_regeneration);
208 Ok(())
209 }
210
211 pub fn remove_patches(&mut self, request: &RemovePatches, disable_uuid_regeneration: bool) -> Result<()> {
218 match request.column {
219 Some(ref column) => self.remove_local_schema_patches_for_table_and_field(&request.table_name, column)?,
220 None => self.remove_local_schema_patches_for_table(&request.table_name)?,
221 }
222
223 self.reload_schema(disable_uuid_regeneration);
224 Ok(())
225 }
226
227 pub fn raw_definitions(&self, table_name: &str, version: Option<i32>) -> Result<RawDefinitions> {
229 let definitions = self.definitions_by_table_name(table_name)?.iter()
230 .filter(|definition| version.is_none_or(|version| *definition.version() == version))
231 .map(|definition| serde_json::to_value(definition.stored()))
232 .collect::<Result<Vec<_>, _>>()?;
233
234 Ok(RawDefinitions { definitions })
235 }
236
237 pub fn set_definition(&mut self, request: &SetDefinition, disable_uuid_regeneration: bool) -> Result<()> {
244 let definition = serde_json::from_value::<Definition>(request.definition.clone())
245 .map_err(|error| ApiError::InvalidParams(format!("Invalid definition: {error}")))?;
246
247 let mut schema = loaded_schema(&self.schema)?.clone();
248 schema.add_definition(&request.table_name, &definition);
249 self.save_schema(schema)?;
250 self.reload_schema(disable_uuid_regeneration);
251 Ok(())
252 }
253
254 pub fn delete_definition_and_save(&mut self, request: &DeleteDefinition, disable_uuid_regeneration: bool) -> Result<()> {
261 let mut schema = loaded_schema(&self.schema)?.clone();
262 schema.remove_definition(&request.table_name, request.version);
263 self.save_schema(schema)?;
264 self.reload_schema(disable_uuid_regeneration);
265 Ok(())
266 }
267
268 pub fn referencing_columns_of(&self, request: &GetReferencingColumns) -> Result<ReferencingColumns> {
270 let table_name = &request.table_name;
271 let version = match request.version {
272 Some(version) => version,
273 None => self.table_definition(&GetTableDefinition { table_name: table_name.to_owned(), version: None })?.version,
274 };
275
276 let definition = loaded_schema(&self.schema)?.definition_by_name_and_version(table_name, version)
277 .ok_or_else(|| ApiError::DefinitionNotFound(table_name.to_owned()))?;
278
279 let columns = self.referencing_columns(table_name, definition)?.into_iter()
280 .map(|(column, tables)| (column, tables.into_iter().collect()))
281 .collect();
282
283 Ok(ReferencingColumns { columns })
284 }
285
286 pub fn import_patches(&mut self, request: &ImportPatches, disable_uuid_regeneration: bool) -> Result<()> {
293 let patches = request.patches.iter()
294 .map(|(table, columns)| (table.clone(), columns.iter()
295 .map(|(column, patch)| (column.clone(), patch.iter().map(|(key, value)| (key.clone(), value.clone())).collect()))
296 .collect()))
297 .collect::<HashMap<String, DefinitionPatch>>();
298
299 self.import_schema_patches(&patches)?;
300 self.reload_schema(disable_uuid_regeneration);
301 Ok(())
302 }
303
304 pub fn definitions_by_table_name(&self, table_name: &str) -> Result<Vec<Definition>> {
306 Ok(loaded_schema(&self.schema)?.definitions_by_table_name(table_name)
307 .map(|definitions| definitions.to_vec())
308 .unwrap_or_default())
309 }
310
311 pub fn definition_patches(&self, table_name: &str, version: i32) -> Result<DefinitionPatch> {
313 let schema = loaded_schema(&self.schema)?;
314 Ok(match schema.definition_by_name_and_version(table_name, version) {
315 Some(definition) => definition.patches().clone(),
316 None => schema.patches().get(table_name).cloned().unwrap_or_default(),
317 })
318 }
319
320 pub fn table_patches(&self, request: &GetTablePatches) -> Result<TablePatches> {
322 let patches = self.definition_patches(&request.table_name, request.version)?.into_iter()
323 .map(|(column, patch)| (column, patch.into_iter().collect()))
324 .collect();
325
326 Ok(TablePatches { patches })
327 }
328
329 pub fn referencing_columns(&self, table_name: &str, definition: &Definition) -> Result<HashMap<String, HashMap<String, Vec<String>>>> {
331 Ok(loaded_schema(&self.schema)?.referencing_columns_for_table(table_name, definition))
332 }
333}