1use anyhow::{anyhow, Result};
15use rayon::prelude::*;
16
17use serde_json::{Number, Value};
18
19use std::collections::BTreeMap;
20
21use std::borrow::Cow;
22use std::collections::{HashMap, HashSet};
23use std::path::Path;
24
25use rpfm_extensions::dependencies::{Dependencies, KEY_DELETES_TABLE_NAME, TableReferences};
26use rpfm_extensions::merge::{db_baseline, delta_merge_db, delta_merge_loc, loc_baseline, MergeConflict, MergeOptions, MergeResolution};
27
28use rpfm_ipc::api::ApiError;
29use rpfm_ipc::api::files::{ASSEMBLY_KIT_TABLE_FILE_NAME, FileRef, FileSource};
30use rpfm_ipc::api::session::{DependencyTableData, DependencyTables};
31use rpfm_ipc::api::references::{DEFAULT_REFERENCE_VALUES_LIMIT, DEFAULT_USAGES_LIMIT, FindUsages, FoundRow, GetReferenceValues, RowLoc, RowLocs, GetTableReferenceData, TableReferenceData, LocSource, ReferenceValue, ReferenceValues, RowLocation, Usage, Usages};
32use rpfm_ipc::api::tables::{AddKeyDeletes, ExportTsv, FilesEdited, ImportTsv, MergeTables, RenameKey, TableUpgraded, TablesMerged, UpgradeTable};
33use rpfm_ipc::api::tables::{ColumnValues, DEFAULT_VALUES_LIMIT, GetColumnValues, GetUnusedNumbers, MAX_UNUSED_NUMBERS, UnusedNumbers, GetTableDefinition, DEFAULT_ROWS_LIMIT, EditTable, FilterOp, GetTableRows, RowEdit, RowFilter, RowRef, TableEdited, TableInfo, TableRow, TableRows};
34use rpfm_ipc::helpers::{DataSource, RFileInfo};
35
36use rpfm_lib::files::{Container, ContainerPath, db::DB, DecodeableExtraData, FileType, pack::Pack, RFile, RFileDecoded, table::{DecodedData, local::TableInMemory, Table}};
37use rpfm_lib::schema::{Definition, Field, FieldType};
38use rpfm_lib::utils::current_time;
39
40use super::{SessionState, loaded_schema, pack, pack_mut};
41
42enum PreparedEdit {
44 Insert(usize, Vec<DecodedData>),
45 Update(usize, Vec<(usize, DecodedData)>),
46
47 Delete(Vec<usize>),
49}
50
51struct PreparedFilter {
53 column_index: usize,
54 op: FilterOp,
55 value: String,
56 ignore_case: bool,
57}
58
59#[derive(Debug)]
61pub enum MergeOutcome {
62
63 Merged(String),
65
66 Conflicts(Vec<MergeConflict>),
68}
69
70enum DeltaMergeOutcome {
72 Merged(RFile),
73 Conflicts(Vec<MergeConflict>),
74}
75
76impl SessionState {
77
78 pub fn merge_files(&mut self, pack_key: &str, paths: &[ContainerPath], merged_path: &str, delete_source_files: bool, options: &MergeOptions) -> Result<MergeOutcome> {
92 let pack = pack_mut(&mut self.packs, pack_key)?;
93 let files_to_merge = pack.files_by_paths(paths, false);
94
95 let merge_result = if *options.delta_merge() {
96 delta_merge_files(&files_to_merge, merged_path, &self.dependencies, options.resolutions())?
97 } else {
98 DeltaMergeOutcome::Merged(RFile::merge(&files_to_merge, merged_path)?)
99 };
100
101 match merge_result {
102 DeltaMergeOutcome::Merged(file) => {
103 let _ = pack.insert(file);
104
105 if delete_source_files {
107 paths.iter()
108 .filter(|path| merged_path != path.path_raw())
109 .for_each(|path| { pack.remove(path); });
110 }
111
112 Ok(MergeOutcome::Merged(merged_path.to_owned()))
113 },
114 DeltaMergeOutcome::Conflicts(conflicts) => Ok(MergeOutcome::Conflicts(conflicts)),
115 }
116 }
117
118 pub fn update_table(&mut self, pack_key: &str, path: &ContainerPath) -> Result<(i32, i32, Vec<String>, Vec<String>)> {
124 let path = path.path_raw();
125 let file = pack_mut(&mut self.packs, pack_key)?.file_mut(path, false)
126 .ok_or_else(|| anyhow!("File not found in the open Pack: {}", path))?;
127
128 let decoded = file.decoded_mut().map_err(|_| anyhow!("File with the following path undecoded: {}", path))?;
129 Ok(self.dependencies.update_db(decoded)?)
130 }
131
132 pub fn export_tsv(&mut self, pack_key: &str, internal_path: &str, external_path: &Path, data_source: DataSource, tsv_keys_first: bool) -> Result<()> {
142 let schema = loaded_schema(&self.schema)?;
143 let file = match data_source {
144 DataSource::PackFile => self.packs.get_mut(pack_key).and_then(|pack| pack.file_mut(internal_path, false)),
145 DataSource::ParentFiles => self.dependencies.file_mut(internal_path, false, true).ok(),
146 DataSource::GameFiles => self.dependencies.file_mut(internal_path, true, false).ok(),
147 DataSource::AssKitFiles => return Err(anyhow!("Exporting a TSV from the Assembly Kit is not yet supported.")),
148 DataSource::ExternalFile => return Err(anyhow!("Exporting a TSV from a external file is not yet supported.")),
149 }.ok_or_else(|| anyhow!("File with the following path not found in the Pack: {}", internal_path))?;
150
151 file.tsv_export_to_path(external_path, schema, tsv_keys_first)?;
152 Ok(())
153 }
154
155 pub fn import_tsv(&mut self, pack_key: &str, internal_path: &str, external_path: &Path) -> Result<RFileDecoded> {
161 let file = pack_mut(&mut self.packs, pack_key)?.file_mut(internal_path, false)
162 .ok_or_else(|| anyhow!("File with the following path not found in the Pack: {}", internal_path))?;
163
164 let original_guid = match file.decoded() {
167 Ok(RFileDecoded::DB(table)) => Some(table.guid().to_owned()),
168 _ => None,
169 };
170
171 let imported = RFile::tsv_import_from_path(external_path, &self.schema)?;
172 let mut decoded = imported.decoded()?.clone();
173 if let (RFileDecoded::DB(table), Some(guid)) = (&mut decoded, original_guid) {
174 table.set_guid(guid);
175 }
176
177 file.set_decoded(decoded.clone())?;
178 Ok(decoded)
179 }
180
181 pub fn cascade_edition(&mut self, pack_key: &str, table_name: &str, definition: &Definition, changes: &[(Field, String, String)]) -> Result<(Vec<ContainerPath>, Vec<RFileInfo>)> {
194 let pack = pack_mut(&mut self.packs, pack_key)?;
195 let edited_paths = match self.schema {
196 Some(ref schema) => changes.iter()
197 .flat_map(|(field, value_before, value_after)| DB::cascade_edition(pack, schema, table_name, field, definition, value_before, value_after))
198 .collect::<Vec<_>>(),
199 None => vec![],
200 };
201
202 let files_info = pack.files_by_paths(&edited_paths, false).into_par_iter().map(From::from).collect();
203 Ok((edited_paths, files_info))
204 }
205
206 pub fn add_keys_to_key_deletes(&mut self, pack_key: &str, table_file_name: &str, key_table_name: &str, keys: &HashSet<String>) -> Result<Option<ContainerPath>> {
219 let path = ContainerPath::File(format!("db/{KEY_DELETES_TABLE_NAME}/{table_file_name}"));
220 let mut files = pack_mut(&mut self.packs, pack_key)?.files_by_type_and_paths_mut(&[FileType::DB], &[path], true);
221
222 let Some(file) = files.first_mut() else { return Ok(None) };
223 let Ok(RFileDecoded::DB(db)) = file.decoded_mut() else { return Ok(None) };
224
225 for key in keys {
226 db.data_mut().push(vec![
227 DecodedData::StringU8(key.to_owned()),
228 DecodedData::StringU8(key_table_name.to_owned()),
229 ]);
230 }
231
232 Ok(Some(file.path_in_container()))
233 }
234
235 pub fn reference_data(&mut self, table_name: &str, definition: &Definition, force_local_generation: bool) -> HashMap<i32, TableReferences> {
247 let Some(ref schema) = self.schema else { return HashMap::new() };
248
249 if force_local_generation || !self.dependencies.local_tables_references().contains_key(table_name) {
250 self.dependencies.generate_local_definition_references(schema, table_name, definition);
251 }
252
253 self.dependencies.db_reference_data(schema, &self.packs, table_name, definition, &None)
254 }
255
256 pub fn table_reference_data(&mut self, request: &GetTableReferenceData) -> Result<TableReferenceData> {
262 let definition = self.definition_by_name_and_version(&request.table_name, request.version)?.clone();
263
264 Ok(TableReferenceData { columns: self.reference_data(&request.table_name, &definition, request.regenerate) })
265 }
266
267 pub fn find_definition(&self, pack_key: Option<&str>, table_name: &str, column: &str, value: &str) -> Result<FoundRow> {
282 if loaded_schema(&self.schema)?.definitions_by_table_name(table_name).is_none_or(|definitions| definitions.is_empty()) {
283 return Err(ApiError::DefinitionNotFound(table_name.to_owned()).into());
284 }
285
286 let table_folders = ContainerPath::db_table_folders(table_name);
287 for (key, pack) in self.packs_to_search(pack_key)? {
288 if let Some(found) = find_in_db_files(&pack.files_by_paths(&table_folders, true), column, value, &FileSource::Pack(key.to_owned())) {
289 return Ok(found);
290 }
291 }
292
293 if pack_key.is_none() {
294 for (source, include_vanilla, include_parent) in [(FileSource::ParentFiles, false, true), (FileSource::GameFiles, true, false)] {
295 if let Ok(files) = self.dependencies.db_data(table_name, include_vanilla, include_parent) {
296 if let Some(found) = find_in_db_files(&files, column, value, &source) {
297 return Ok(found);
298 }
299 }
300 }
301
302 if let Some(table) = self.dependencies.asskit_only_db_tables().get(table_name) {
303 if let Some((column_index, row_index)) = find_in_db(table, column, value) {
304 let path = format!("db/{table_name}/{ASSEMBLY_KIT_TABLE_FILE_NAME}");
305 let location = RowLocation { source: FileSource::AssemblyKit, path, column_index, row_index };
306 return Ok(FoundRow { location, values: row_values_by_column(table.table(), row_index) });
307 }
308 }
309 }
310
311 Err(ApiError::NotFound(format!("The value {value} of the column {column} of {table_name}")).into())
312 }
313
314 pub fn find_loc(&self, pack_key: Option<&str>, loc_key: &str) -> Result<FoundRow> {
327 for (key, pack) in self.packs_to_search(pack_key)? {
328 if let Some(found) = find_in_loc_files(&pack.files_by_type(&[FileType::Loc]), loc_key, &FileSource::Pack(key.to_owned())) {
329 return Ok(found);
330 }
331 }
332
333 if pack_key.is_none() {
334 for (source, include_vanilla, include_parent) in [(FileSource::ParentFiles, false, true), (FileSource::GameFiles, true, false)] {
335 if let Ok(files) = self.dependencies.loc_data(include_vanilla, include_parent) {
336 if let Some(found) = find_in_loc_files(&files, loc_key, &source) {
337 return Ok(found);
338 }
339 }
340 }
341 }
342
343 Err(ApiError::NotFound(format!("The loc key {loc_key}")).into())
344 }
345
346 pub fn row_locs(&mut self, row_ref: &RowRef) -> Result<RowLocs> {
352 let table_name = row_ref.file.path.strip_prefix("db/")
353 .and_then(|path| path.split('/').next())
354 .ok_or_else(|| ApiError::NotATable(row_ref.file.path.clone()))?
355 .to_owned();
356
357 let table = self.table(&row_ref.file)?;
358 let definition = table.definition();
359 let data = table.data();
360 let row = data.get(row_ref.index)
361 .ok_or_else(|| ApiError::InvalidParams(format!("Row {} of {} doesn't exist.", row_ref.index, row_ref.file.path)))?;
362
363 if definition.localised_fields().is_empty() {
364 return Ok(RowLocs::default());
365 }
366
367 if definition.localised_key_order().is_empty() {
368 return Err(ApiError::InvalidParams(format!("The schema doesn't say which columns make the loc keys of {table_name}.")).into());
369 }
370
371 let key = definition.localised_key_order().iter().map(|index| row[*index as usize].data_to_string()).collect::<String>();
372 let table_name = table_name.strip_suffix("_tables").unwrap_or(&table_name);
373 let keys = definition.localised_fields().iter()
374 .map(|field| (field.name().to_owned(), format!("{table_name}_{}_{key}", field.name())))
375 .collect::<Vec<_>>();
376
377 let locs = keys.into_iter()
378 .map(|(column, key)| {
379 let text = self.find_loc(None, &key).ok()
380 .and_then(|found| found.values.get("text").and_then(Value::as_str).map(str::to_owned));
381 RowLoc { column, key, text }
382 })
383 .collect();
384
385 Ok(RowLocs { locs })
386 }
387
388 fn packs_to_search<'a>(&'a self, pack_key: Option<&'a str>) -> Result<Vec<(&'a str, &'a Pack)>> {
395 Ok(match pack_key {
396 Some(pack_key) => vec![(pack_key, pack(&self.packs, pack_key)?)],
397 None => self.packs.iter().map(|(key, pack)| (key.as_str(), pack)).collect(),
398 })
399 }
400
401 pub fn search_references(&self, pack_key: Option<&str>, reference_map: &HashMap<String, Vec<String>>, value: &str) -> Result<Vec<Usage>> {
413 let paths = reference_map.keys().flat_map(|table_name| ContainerPath::db_table_folders(table_name)).collect::<Vec<ContainerPath>>();
414 let packs = self.packs_to_search(pack_key)?;
415
416 let mut references = vec![];
417 for (key, pack) in packs {
418 let files = pack.files_by_paths(&paths, true);
419 let source = FileSource::Pack(key.to_owned());
420 for (table_name, columns) in reference_map {
421 for file in &files {
422 if file.db_table_name_from_path() == Some(table_name.as_str()) {
423 references.extend(references_in_file(file, columns, value, &source));
424 }
425 }
426 }
427 }
428
429 if pack_key.is_some() {
430 return Ok(references);
431 }
432
433 for (source, include_vanilla, include_parent) in [(FileSource::ParentFiles, false, true), (FileSource::GameFiles, true, false)] {
434 for (table_name, columns) in reference_map {
435 if let Ok(tables) = self.dependencies.db_data(table_name, include_vanilla, include_parent) {
436 references.par_extend(tables.par_iter().flat_map(|table| references_in_file(table, columns, value, &source)));
437 }
438 }
439 }
440
441 Ok(references)
442 }
443
444 pub fn find_usages(&self, request: &FindUsages) -> Result<Usages> {
446 let schema = loaded_schema(&self.schema)?;
447 let version = match request.version {
448 Some(version) => version,
449 None => self.table_definition(&GetTableDefinition { table_name: request.table_name.clone(), version: None })?.version,
450 };
451
452 let definition = self.definition_by_name_and_version(&request.table_name, version)?;
453 let reference_map = schema.referencing_columns_for_table(&request.table_name, definition)
454 .remove(&request.column)
455 .unwrap_or_default();
456
457 let usages = self.search_references(request.pack.as_deref(), &reference_map, &request.value)?;
458 let total = usages.len();
459 let usages = usages.into_iter()
460 .skip(request.offset)
461 .take(request.limit.unwrap_or(DEFAULT_USAGES_LIMIT))
462 .collect();
463
464 Ok(Usages { usages, total })
465 }
466
467 pub fn loc_key_source(&self, loc_key: &str) -> Option<(String, String, Vec<String>)> {
469 self.dependencies.loc_key_source(loc_key)
470 }
471
472 pub fn column_values(&self, table_name: &str, column_name: &str, include_packs: bool, include_dependencies: bool) -> HashSet<String> {
481 let packs = if include_packs { Some(&self.packs) } else { None };
482 self.dependencies.db_values_from_table_name_and_column_name(packs, table_name, column_name, include_dependencies, include_dependencies)
483 }
484
485 pub fn column_values_page(&self, request: &GetColumnValues) -> Result<ColumnValues> {
491 let definition = self.table_definition(&GetTableDefinition { table_name: request.table_name.clone(), version: None })?;
492 if !definition.columns.iter().any(|column| column.name == request.column) {
493 return Err(ApiError::InvalidParams(format!("The table has no column named {}.", request.column)).into());
494 }
495
496 let mut values = self.column_values(&request.table_name, &request.column, request.include_packs, request.include_dependencies)
497 .into_iter()
498 .filter(|value| value.starts_with(&request.prefix))
499 .collect::<Vec<_>>();
500 values.sort_unstable();
501
502 let total = values.len();
503 let values = values.into_iter()
504 .skip(request.offset)
505 .take(request.limit.unwrap_or(DEFAULT_VALUES_LIMIT))
506 .collect();
507
508 Ok(ColumnValues { values, total })
509 }
510
511 pub fn unused_numbers(&self, request: &GetUnusedNumbers) -> Result<UnusedNumbers> {
518 if request.count > MAX_UNUSED_NUMBERS {
519 return Err(ApiError::InvalidParams(format!("Up to {MAX_UNUSED_NUMBERS} numbers can be requested at once.")).into());
520 }
521
522 let definition = self.table_definition(&GetTableDefinition { table_name: request.table_name.clone(), version: None })?;
523 let column = definition.columns.iter()
524 .find(|column| column.name == request.column)
525 .ok_or_else(|| ApiError::InvalidParams(format!("The table has no column named {}.", request.column)))?;
526
527 let highest_allowed = match column.field_type {
528 FieldType::I16 | FieldType::OptionalI16 => i64::from(i16::MAX),
529 FieldType::I32 | FieldType::OptionalI32 => i64::from(i32::MAX),
530 FieldType::I64 | FieldType::OptionalI64 => i64::MAX,
531 FieldType::Boolean |
532 FieldType::F32 |
533 FieldType::F64 |
534 FieldType::ColourRGB |
535 FieldType::StringU8 |
536 FieldType::StringU16 |
537 FieldType::OptionalStringU8 |
538 FieldType::OptionalStringU16 |
539 FieldType::SequenceU16(_) |
540 FieldType::SequenceU32(_) => return Err(ApiError::InvalidParams(format!("The column {} is not an integer column.", request.column)).into()),
541 };
542
543 let highest_used = self.column_values(&request.table_name, &request.column, true, true).iter()
544 .filter_map(|value| value.parse::<i64>().ok())
545 .max()
546 .unwrap_or(0);
547
548 let numbers = (1..=request.count as i64)
549 .map(|offset| highest_used.checked_add(offset).filter(|number| *number <= highest_allowed))
550 .collect::<Option<Vec<_>>>()
551 .ok_or_else(|| ApiError::InvalidParams(format!("The column {} has less than {} numbers left above {highest_used}.", request.column, request.count)))?;
552
553 Ok(UnusedNumbers { numbers })
554 }
555
556 pub fn reference_values(&mut self, request: &GetReferenceValues) -> Result<ReferenceValues> {
562 let version = self.table_definition(&GetTableDefinition { table_name: request.table_name.clone(), version: None })?.version;
563 let definition = loaded_schema(&self.schema)?.definition_by_name_and_version(&request.table_name, version)
564 .ok_or_else(|| ApiError::DefinitionNotFound(request.table_name.clone()))?
565 .clone();
566
567 let column_index = definition.fields_processed().iter()
568 .position(|field| field.name() == request.column)
569 .ok_or_else(|| ApiError::InvalidParams(format!("The table has no column named {}.", request.column)))?;
570
571 let references = self.reference_data(&request.table_name, &definition, false).remove(&(column_index as i32))
572 .ok_or_else(|| ApiError::InvalidParams(format!("The column {} is not a reference column.", request.column)))?;
573
574 let mut values = references.data().iter()
575 .filter(|(value, _)| value.starts_with(&request.prefix))
576 .map(|(value, lookup)| ReferenceValue { value: value.clone(), lookup: lookup.clone() })
577 .collect::<Vec<_>>();
578 values.sort_unstable_by(|a, b| a.value.cmp(&b.value));
579
580 let total = values.len();
581 let values = values.into_iter()
582 .skip(request.offset)
583 .take(request.limit.unwrap_or(DEFAULT_REFERENCE_VALUES_LIMIT))
584 .collect();
585
586 Ok(ReferenceValues { values, total })
587 }
588
589 pub fn loc_source(&self, loc_key: &str) -> Option<LocSource> {
591 self.loc_key_source(loc_key).map(|(table, column, key_values)| LocSource { table_name: format!("{table}_tables"), column, key_values })
592 }
593
594 pub fn dependency_table_names(&self) -> Vec<String> {
596 self.dependencies.vanilla_loose_tables().keys()
597 .chain(self.dependencies.vanilla_tables().keys())
598 .cloned()
599 .collect()
600 }
601
602 pub fn dependency_table_version(&self, table_name: &str) -> Result<i32> {
606 if !self.dependencies.is_vanilla_data_loaded(false) {
607 return Err(ApiError::DependenciesNotLoaded.into());
608 }
609
610 if let Some(version) = self.dependencies.db_version(table_name) {
611 return Ok(version);
612 }
613
614 if !(table_name.starts_with("start_pos_") || table_name.starts_with("twad_") || table_name.starts_with("ceo")) {
615 return Err(anyhow!("Table not found in the game files."));
616 }
617
618 loaded_schema(&self.schema)?.definitions_by_table_name(table_name)
619 .and_then(|definitions| definitions.first())
620 .map(|definition| *definition.version())
621 .ok_or_else(|| anyhow!("There are no definitions for this specific table."))
622 }
623
624 pub fn dependency_tables(&self, table_name: &str) -> Result<Vec<RFile>> {
626 Ok(self.dependencies.db_data(table_name, true, true)?.into_iter().cloned().collect())
627 }
628
629 pub fn dependency_table_versions(&self) -> DependencyTables {
631 let names = self.dependency_table_names().into_iter().chain(self.custom_table_names().unwrap_or_default());
632 let tables = names.filter_map(|name| self.dependency_table_version(&name).ok().map(|version| (name, version))).collect();
633 DependencyTables { tables }
634 }
635
636 pub fn dependency_table_data(&self, table_name: &str) -> Result<DependencyTableData> {
638 let tables = self.dependency_tables(table_name)?.iter()
639 .filter_map(|file| match file.decoded() {
640 Ok(RFileDecoded::DB(table)) => Some(table.clone()),
641 _ => None,
642 })
643 .collect();
644
645 Ok(DependencyTableData { tables })
646 }
647}
648
649impl SessionState {
650
651 pub fn table_info(&mut self, file: &FileRef) -> Result<TableInfo> {
653 let table = self.table(file)?;
654 Ok(TableInfo {
655 table_name: table.name().to_owned(),
656 version: *table.definition().version(),
657 columns: table.definition().fields_processed().to_vec(),
658 row_count: table.len(),
659 })
660 }
661
662 pub fn table_rows(&mut self, request: &GetTableRows) -> Result<TableRows> {
668 let table = self.table(&request.file)?;
669 let fields = table.definition().fields_processed();
670 let column_index = |name: &str| fields.iter()
671 .position(|field| field.name() == name)
672 .ok_or_else(|| ApiError::InvalidParams(format!("The table has no column named {name}.")));
673
674 let columns = match request.columns {
675 Some(ref names) => names.iter()
676 .map(|name| column_index(name).map(|index| (index, name.to_owned())))
677 .collect::<Result<Vec<_>, _>>()?,
678 None => fields.iter().enumerate().map(|(index, field)| (index, field.name().to_owned())).collect(),
679 };
680
681 let filters = request.filters.iter()
682 .map(|filter| column_index(&filter.column).map(|column_index| PreparedFilter::new(filter, column_index)))
683 .collect::<Result<Vec<_>, _>>()?;
684
685 let limit = request.limit.unwrap_or(DEFAULT_ROWS_LIMIT);
686 let mut rows = vec![];
687 let mut total = 0;
688
689 for (index, row) in table.data().iter().enumerate() {
690 if !filters.iter().all(|filter| filter.matches(row)) {
691 continue;
692 }
693
694 if total >= request.offset && rows.len() < limit {
695 let values = columns.iter()
696 .map(|(column_index, _)| row.get(*column_index).map(decoded_to_json).unwrap_or(Value::Null))
697 .collect();
698
699 rows.push(TableRow { index, values });
700 }
701
702 total += 1;
703 }
704
705 Ok(TableRows {
706 columns: columns.into_iter().map(|(_, name)| name).collect(),
707 rows,
708 total,
709 })
710 }
711
712 pub fn edit_table(&mut self, request: &EditTable) -> Result<TableEdited> {
719 let file = FileRef { source: FileSource::Pack(request.pack.clone()), path: request.path.clone() };
720 let fields = self.table(&file)?.definition().fields_processed().to_vec();
721 let copied = self.copied_row_values(&request.edits, &fields)?;
722
723 let row_count = match self.decoded_table_file(&file)?.decoded_mut()? {
724 RFileDecoded::DB(table) => {
725 let edits = prepare_row_edits(&request.edits, &fields, table.new_row(), table.data().len(), &copied)?;
726 apply_row_edits(table.data_mut(), edits)
727 }
728 RFileDecoded::Loc(table) => {
729 let edits = prepare_row_edits(&request.edits, &fields, table.new_row(), table.data().len(), &copied)?;
730 apply_row_edits(table.data_mut(), edits)
731 }
732 _ => return Err(ApiError::NotATable(request.path.clone()).into()),
733 };
734
735 Ok(TableEdited { row_count })
736 }
737
738 fn copied_row_values(&mut self, edits: &[RowEdit], fields: &[Field]) -> Result<BTreeMap<usize, Vec<(usize, DecodedData)>>> {
749 let mut copied = BTreeMap::new();
750 for (edit_index, edit) in edits.iter().enumerate() {
751 let RowEdit::Insert { copy_from: Some(row_ref), .. } = edit else {
752 continue;
753 };
754
755 let invalid = |message: String| ApiError::InvalidParams(format!("Edit {edit_index}: {message}"));
756 let table = self.table(&row_ref.file)?;
757 let source_fields = table.definition().fields_processed();
758 let data = table.data();
759 let row = data.get(row_ref.index)
760 .ok_or_else(|| invalid(format!("row {} of {} doesn't exist.", row_ref.index, row_ref.file.path)))?;
761
762 let values = fields.iter().enumerate()
763 .filter_map(|(column_index, field)| {
764 let source_index = source_fields.iter().position(|source_field| source_field.name() == field.name())?;
765 Some(copied_value(field.field_type(), &row[source_index])
766 .map(|value| (column_index, value))
767 .map_err(|_| invalid(format!("the value of the column {} can't be copied, as its type is different.", field.name()))))
768 })
769 .collect::<Result<Vec<_>, _>>()?;
770
771 copied.insert(edit_index, values);
772 }
773
774 Ok(copied)
775 }
776
777 pub fn merge_tables(&mut self, request: &MergeTables) -> Result<TablesMerged> {
783 let paths = self.pack_container_paths(&request.pack, &request.paths)?;
784 let mut options = MergeOptions::default();
785 options.set_delta_merge(request.delta);
786 options.set_resolutions(request.resolutions.clone());
787
788 Ok(match self.merge_files(&request.pack, &paths, &request.merged_path, request.delete_sources, &options)? {
789 MergeOutcome::Merged(path) => TablesMerged { merged: Some(path), conflicts: vec![] },
790 MergeOutcome::Conflicts(conflicts) => TablesMerged { merged: None, conflicts },
791 })
792 }
793
794 pub fn upgrade_table(&mut self, request: &UpgradeTable) -> Result<TableUpgraded> {
796 let (old_version, new_version, deleted_columns, added_columns) = self.update_table(&request.pack, &ContainerPath::File(request.path.clone()))?;
797 Ok(TableUpgraded { old_version, new_version, deleted_columns, added_columns })
798 }
799
800 pub fn rename_key(&mut self, request: &RenameKey) -> Result<FilesEdited> {
806 let version = match request.version {
807 Some(version) => version,
808 None => self.table_definition(&GetTableDefinition { table_name: request.table_name.clone(), version: None })?.version,
809 };
810
811 let definition = self.definition_by_name_and_version(&request.table_name, version)?.clone();
812 let field = definition.fields_processed().iter()
813 .find(|field| field.name() == request.column)
814 .cloned()
815 .ok_or_else(|| ApiError::InvalidParams(format!("The table has no column named {}.", request.column)))?;
816
817 let changes = [(field, request.old_value.clone(), request.new_value.clone())];
818 let (edited, _) = self.cascade_edition(&request.pack, &request.table_name, &definition, &changes)?;
819 Ok(FilesEdited { edited: edited.iter().map(|path| path.path_raw().to_owned()).collect() })
820 }
821
822 pub fn add_key_deletes(&mut self, request: &AddKeyDeletes) -> Result<FilesEdited> {
828 let keys = request.keys.iter().cloned().collect::<HashSet<_>>();
829 let path = self.add_keys_to_key_deletes(&request.pack, &request.file_name, &request.table_name, &keys)?
830 .ok_or_else(|| ApiError::FileNotFound(format!("db/{KEY_DELETES_TABLE_NAME}/{}", request.file_name)))?;
831
832 Ok(FilesEdited { edited: vec![path.path_raw().to_owned()] })
833 }
834
835 pub fn export_table_tsv(&mut self, request: &ExportTsv, keys_first: bool) -> Result<()> {
842 let (pack_key, data_source) = match request.file.source {
843 FileSource::Pack(ref pack_key) => (pack_key.as_str(), DataSource::PackFile),
844 FileSource::GameFiles => ("", DataSource::GameFiles),
845 FileSource::ParentFiles => ("", DataSource::ParentFiles),
846 FileSource::AssemblyKit => return Err(ApiError::InvalidParams("Assembly Kit tables can't be exported to TSV.".to_owned()).into()),
847 };
848
849 self.export_tsv(pack_key, &request.file.path, &request.destination, data_source, keys_first)
850 }
851
852 pub fn import_table_tsv(&mut self, request: &ImportTsv) -> Result<TableEdited> {
854 let row_count = match self.import_tsv(&request.pack, &request.path, &request.tsv_path)? {
855 RFileDecoded::DB(table) => table.len(),
856 RFileDecoded::Loc(table) => table.len(),
857 _ => return Err(ApiError::NotATable(request.path.clone()).into()),
858 };
859
860 Ok(TableEdited { row_count })
861 }
862
863 fn table(&mut self, file: &FileRef) -> Result<&TableInMemory> {
865 self.check_file_exists(file)?;
866
867 if file.source == FileSource::AssemblyKit {
868 let not_found = || ApiError::FileNotFound(file.path.clone());
869 let table_name = file.path.split('/').nth(1).ok_or_else(not_found)?;
870 let table = self.dependencies.asskit_only_db_tables().get(table_name).ok_or_else(not_found)?;
871 return Ok(table.table());
872 }
873
874 match self.decoded_table_file(file)?.decoded()? {
875 RFileDecoded::DB(table) => Ok(table.table()),
876 RFileDecoded::Loc(table) => Ok(table.table()),
877 _ => Err(ApiError::NotATable(file.path.clone()).into()),
878 }
879 }
880
881 fn decoded_table_file(&mut self, file: &FileRef) -> Result<&mut RFile> {
883 let rfile = match file.source {
884 FileSource::Pack(ref pack_key) => pack_mut(&mut self.packs, pack_key)?.files_mut().get_mut(&file.path),
885 FileSource::GameFiles => self.dependencies.file_mut(&file.path, true, false).ok(),
886 FileSource::ParentFiles => self.dependencies.file_mut(&file.path, false, true).ok(),
887
888 FileSource::AssemblyKit => return Err(ApiError::ReadOnly(file.path.clone()).into()),
890 }.ok_or_else(|| ApiError::FileNotFound(file.path.clone()))?;
891
892 let file_type = rfile.file_type();
893 if file_type != FileType::DB && file_type != FileType::Loc {
894 return Err(ApiError::NotATable(file.path.clone()).into());
895 }
896
897 if file_type == FileType::DB && self.schema.is_none() {
898 return Err(ApiError::SchemaNotLoaded.into());
899 }
900
901 let mut extra_data = DecodeableExtraData::default();
902 extra_data.set_schema(self.schema.as_ref());
903 rfile.decode(&Some(extra_data), true, false)?;
904 Ok(rfile)
905 }
906}
907
908impl PreparedFilter {
909
910 fn new(filter: &RowFilter, column_index: usize) -> Self {
912 Self {
913 column_index,
914 op: filter.op,
915 value: if filter.ignore_case { filter.value.to_lowercase() } else { filter.value.clone() },
916 ignore_case: filter.ignore_case,
917 }
918 }
919
920 fn matches(&self, row: &[DecodedData]) -> bool {
922 let Some(data) = row.get(self.column_index) else { return false };
923 let value = data.data_to_string();
924 let value = if self.ignore_case { Cow::Owned(value.to_lowercase()) } else { value };
925
926 match self.op {
927 FilterOp::Equals => *value == self.value,
928 FilterOp::NotEquals => *value != self.value,
929 FilterOp::Contains => value.contains(&self.value),
930 FilterOp::StartsWith => value.starts_with(&self.value),
931 FilterOp::EndsWith => value.ends_with(&self.value),
932 }
933 }
934}
935
936fn prepare_row_edits(edits: &[RowEdit], fields: &[Field], new_row: Vec<DecodedData>, mut row_count: usize, copied: &BTreeMap<usize, Vec<(usize, DecodedData)>>) -> Result<Vec<PreparedEdit>, ApiError> {
946 let mut prepared = Vec::with_capacity(edits.len());
947 for (edit_index, edit) in edits.iter().enumerate() {
948 let invalid = |message: String| ApiError::InvalidParams(format!("Edit {edit_index}: {message}"));
949 let missing_row = |index: usize, row_count: usize| invalid(format!("row {index} doesn't exist, the table has {row_count} rows at this point."));
950
951 match edit {
952 RowEdit::Insert { index, values, .. } => {
953 let index = index.unwrap_or(row_count);
954 if index > row_count {
955 return Err(missing_row(index, row_count));
956 }
957
958 let mut row = new_row.clone();
959 let copied_values = copied.get(&edit_index).into_iter().flatten().cloned();
960 for (column_index, value) in copied_values.chain(row_values(fields, values).map_err(invalid)?) {
961 row[column_index] = value;
962 }
963
964 prepared.push(PreparedEdit::Insert(index, row));
965 row_count += 1;
966 }
967 RowEdit::Update { index, values } => {
968 if *index >= row_count {
969 return Err(missing_row(*index, row_count));
970 }
971
972 prepared.push(PreparedEdit::Update(*index, row_values(fields, values).map_err(invalid)?));
973 }
974 RowEdit::Delete { indexes } => {
975 let mut indexes = indexes.clone();
976 indexes.sort_unstable_by(|a, b| b.cmp(a));
977 indexes.dedup();
978
979 if let Some(index) = indexes.first().filter(|index| **index >= row_count) {
980 return Err(missing_row(*index, row_count));
981 }
982
983 row_count -= indexes.len();
984 prepared.push(PreparedEdit::Delete(indexes));
985 }
986 }
987 }
988
989 Ok(prepared)
990}
991
992fn apply_row_edits(rows: &mut Vec<Vec<DecodedData>>, edits: Vec<PreparedEdit>) -> usize {
998 for edit in edits {
999 match edit {
1000 PreparedEdit::Insert(index, row) => rows.insert(index, row),
1001 PreparedEdit::Update(index, values) => {
1002 for (column_index, value) in values {
1003 rows[index][column_index] = value;
1004 }
1005 }
1006 PreparedEdit::Delete(indexes) => {
1007 for index in indexes {
1008 rows.remove(index);
1009 }
1010 }
1011 }
1012 }
1013
1014 rows.len()
1015}
1016
1017fn row_values(fields: &[Field], values: &BTreeMap<String, Value>) -> Result<Vec<(usize, DecodedData)>, String> {
1023 values.iter()
1024 .map(|(column, value)| {
1025 let column_index = fields.iter()
1026 .position(|field| field.name() == column)
1027 .ok_or_else(|| format!("the table has no column named {column}."))?;
1028
1029 let value = json_to_decoded(fields[column_index].field_type(), value)
1030 .map_err(|error| format!("invalid value for column {column}: {error}"))?;
1031
1032 Ok((column_index, value))
1033 })
1034 .collect()
1035}
1036
1037fn copied_value(field_type: &FieldType, value: &DecodedData) -> Result<DecodedData> {
1039 if value.is_field_type_correct(field_type) {
1040 Ok(value.clone())
1041 } else {
1042 Ok(DecodedData::new_from_type_and_string(field_type, &value.data_to_string())?)
1043 }
1044}
1045
1046fn json_to_decoded(field_type: &FieldType, value: &Value) -> Result<DecodedData> {
1050 let text = match value {
1051 Value::String(text) => Cow::Borrowed(text.as_str()),
1052 Value::Bool(_) | Value::Number(_) => Cow::Owned(value.to_string()),
1053 Value::Null | Value::Array(_) | Value::Object(_) => return Err(anyhow!("expected a boolean, number or string, found {value}.")),
1054 };
1055
1056 DecodedData::new_from_type_and_string(field_type, &text)
1057 .map_err(|_| anyhow!("expected {}, found {value}.", expected_value(field_type)))
1058}
1059
1060fn expected_value(field_type: &FieldType) -> &'static str {
1062 match field_type {
1063 FieldType::Boolean => "a boolean",
1064 FieldType::F32 | FieldType::F64 => "a number",
1065 FieldType::I16 | FieldType::OptionalI16 => "a 16-bit integer",
1066 FieldType::I32 | FieldType::OptionalI32 => "a 32-bit integer",
1067 FieldType::I64 | FieldType::OptionalI64 => "a 64-bit integer",
1068 FieldType::ColourRGB => "a hexadecimal colour, like FF0000",
1069 FieldType::StringU8 |
1070 FieldType::StringU16 |
1071 FieldType::OptionalStringU8 |
1072 FieldType::OptionalStringU16 |
1073 FieldType::SequenceU16(_) |
1074 FieldType::SequenceU32(_) => "a string",
1075 }
1076}
1077
1078fn decoded_to_json(data: &DecodedData) -> Value {
1082 match data {
1083 DecodedData::Boolean(value) => Value::Bool(*value),
1084
1085 DecodedData::F32(value) => value.to_string().parse::<f64>().ok().and_then(Number::from_f64).map_or(Value::Null, Value::Number),
1087 DecodedData::F64(value) => Number::from_f64(*value).map_or(Value::Null, Value::Number),
1088 DecodedData::I16(value) | DecodedData::OptionalI16(value) => Value::from(*value),
1089 DecodedData::I32(value) | DecodedData::OptionalI32(value) => Value::from(*value),
1090 DecodedData::I64(value) | DecodedData::OptionalI64(value) => Value::from(*value),
1091 DecodedData::ColourRGB(value) |
1092 DecodedData::StringU8(value) |
1093 DecodedData::StringU16(value) |
1094 DecodedData::OptionalStringU8(value) |
1095 DecodedData::OptionalStringU16(value) => Value::String(value.clone()),
1096 DecodedData::SequenceU16(_) |
1097 DecodedData::SequenceU32(_) => Value::String(data.data_to_string().into_owned()),
1098 }
1099}
1100
1101fn find_in_db(table: &DB, column_name: &str, value: &str) -> Option<(usize, usize)> {
1109 let definition = table.definition();
1110 let key_column_name = if definition.localised_fields().iter().any(|field| field.name() == column_name) {
1111 definition.localised_key_order().first()
1112 .and_then(|index| definition.fields_processed().get(*index as usize).map(|field| field.name().to_owned()))
1113 } else {
1114 None
1115 };
1116
1117 let column_name = key_column_name.as_deref().unwrap_or(column_name);
1118 let (column_index, row_indexes) = table.table().rows_containing_data(column_name, value)?;
1119 row_indexes.first().map(|row_index| (column_index, *row_index))
1120}
1121
1122fn find_in_db_files(files: &[&RFile], column_name: &str, value: &str, source: &FileSource) -> Option<FoundRow> {
1124 files.iter().find_map(|file| match file.decoded() {
1125 Ok(RFileDecoded::DB(table)) => find_in_db(table, column_name, value)
1126 .map(|(column_index, row_index)| FoundRow {
1127 location: RowLocation { source: source.clone(), path: file.path_in_container_raw().to_owned(), column_index, row_index },
1128 values: row_values_by_column(table.table(), row_index),
1129 }),
1130 _ => None,
1131 })
1132}
1133
1134fn find_in_loc_files(files: &[&RFile], loc_key: &str, source: &FileSource) -> Option<FoundRow> {
1136 files.iter().find_map(|file| match file.decoded() {
1137 Ok(RFileDecoded::Loc(table)) => {
1138 let (column_index, row_indexes) = table.table().rows_containing_data("key", loc_key)?;
1139 row_indexes.first().map(|row_index| FoundRow {
1140 location: RowLocation { source: source.clone(), path: file.path_in_container_raw().to_owned(), column_index, row_index: *row_index },
1141 values: row_values_by_column(table.table(), *row_index),
1142 })
1143 }
1144 _ => None,
1145 })
1146}
1147
1148fn row_values_by_column(table: &TableInMemory, row_index: usize) -> BTreeMap<String, Value> {
1150 let data = table.data();
1151 let Some(row) = data.get(row_index) else {
1152 return BTreeMap::new();
1153 };
1154
1155 table.definition().fields_processed().iter()
1156 .zip(row)
1157 .map(|(field, value)| (field.name().to_owned(), decoded_to_json(value)))
1158 .collect()
1159}
1160
1161fn references_in_file(file: &RFile, columns: &[String], value: &str, source: &FileSource) -> Vec<Usage> {
1163 let Ok(RFileDecoded::DB(table)) = file.decoded() else { return vec![] };
1164
1165 columns.iter()
1166 .filter_map(|column_name| table.table().rows_containing_data(column_name, value).map(|found| (column_name, found)))
1167 .flat_map(|(column_name, (column_index, row_indexes))| row_indexes.into_iter()
1168 .map(|row_index| Usage {
1169 location: RowLocation { source: source.clone(), path: file.path_in_container_raw().to_owned(), column_index, row_index },
1170 column: column_name.to_owned(),
1171 values: row_values_by_column(table.table(), row_index),
1172 })
1173 .collect::<Vec<_>>())
1174 .collect()
1175}
1176
1177fn delta_merge_files(sources: &[&RFile], merged_path: &str, dependencies: &Dependencies, resolutions: &[MergeResolution]) -> Result<DeltaMergeOutcome> {
1180 if sources.len() < 2 {
1181 return Err(anyhow!("Not enough tables provided to merge."));
1182 }
1183
1184 let (merged, conflicts) = match sources[0].decoded()? {
1185 RFileDecoded::DB(_) => {
1186 let tables = sources.iter()
1187 .filter_map(|file| if let Ok(RFileDecoded::DB(table)) = file.decoded() { Some((file.path_in_container_raw(), table)) } else { None })
1188 .collect::<Vec<_>>();
1189
1190 let baseline = db_baseline(dependencies, tables[0].1.table_name());
1191 let (merged, conflicts) = delta_merge_db(&tables, baseline.as_ref(), resolutions)?;
1192 (RFileDecoded::DB(merged), conflicts)
1193 },
1194 RFileDecoded::Loc(_) => {
1195 let tables = sources.iter()
1196 .filter_map(|file| if let Ok(RFileDecoded::Loc(table)) = file.decoded() { Some((file.path_in_container_raw(), table)) } else { None })
1197 .collect::<Vec<_>>();
1198
1199 let baseline = loc_baseline(dependencies);
1200 let (merged, conflicts) = delta_merge_loc(&tables, baseline.as_ref(), resolutions)?;
1201 (RFileDecoded::Loc(merged), conflicts)
1202 },
1203 _ => return Err(anyhow!("Delta merge is only supported for DB and Loc tables.")),
1204 };
1205
1206 if conflicts.is_empty() {
1207 Ok(DeltaMergeOutcome::Merged(RFile::new_from_decoded(&merged, current_time()?, merged_path)))
1208 } else {
1209 Ok(DeltaMergeOutcome::Conflicts(conflicts))
1210 }
1211}
1212
1213#[cfg(test)]
1218#[path = "tables_test.rs"]
1219mod tables_test;