1use anyhow::{anyhow, Result};
31
32use itertools::Itertools;
33use open::that;
34use rayon::prelude::*;
35
36use std::collections::{BTreeMap, HashMap, HashSet};
37use std::env::temp_dir;
38use std::fs::{DirBuilder, File};
39use std::io::{BufWriter, Cursor, Write};
40use std::path::PathBuf;
41use std::slice::from_ref;
42use std::sync::{Arc, RwLock};
43use std::thread;
44use std::time::SystemTime;
45
46use rpfm_extensions::dependencies::*;
47use rpfm_extensions::diagnostics::Diagnostics;
48use rpfm_extensions::gltf::{gltf_from_rigid, save_gltf_to_disk};
49use rpfm_extensions::optimizer::OptimizableContainer;
50use rpfm_extensions::translator::PackTranslation;
51
52use rpfm_ipc::helpers::*;
53use rpfm_ipc::messages::OperationalMode;
54use rpfm_ipc::settings_keys::*;
55
56use rpfm_lib::compression::CompressionFormat;
57use rpfm_lib::files::{animpack::AnimPack, Container, ContainerPath, db::DB, DecodeableExtraData, EncodeableExtraData, FileType, loc::Loc, pack::*, portrait_settings::PortraitSettings, RFile, RFileDecoded, table::{DecodedData, Table}, text::*};
58use rpfm_lib::games::{GameInfo, LUA_REPO, LUA_BRANCH, LUA_REMOTE, OLD_AK_REPO, OLD_AK_BRANCH, OLD_AK_REMOTE, pfh_file_type::PFHFileType, supported_games::*, VanillaDBTableNameLogic};
59use rpfm_lib::games::{TRANSLATIONS_REPO, TRANSLATIONS_BRANCH, TRANSLATIONS_REMOTE};
60use rpfm_lib::integrations::{assembly_kit::*, git::*};
61use rpfm_lib::schema::*;
62use rpfm_lib::utils::*;
63
64use rpfm_telemetry::*;
65
66use crate::*;
67use crate::ceo_builder::{build_ceo_entries, build_ceo_post, get_trait_ceos};
68use crate::comms::CentralCommand;
69use crate::session::Session;
70use crate::settings::*;
71use crate::updater;
72
73use tokio::sync::mpsc::{UnboundedReceiver, UnboundedSender};
74
75pub const VANILLA_LOC_NAME: &str = "vanilla_english.tsv";
85
86pub const VANILLA_FIXES_NAME: &str = "vanilla_fixes_";
96
97const DEFAULT_PACK_STEM: &str = "new_pack";
100
101const DEFAULT_PACK_EXT: &str = ".pack";
104
105fn command_name(cmd: &Command) -> String {
110 struct NameOnly {
111 out: String,
112 done: bool,
113 }
114
115 impl std::fmt::Write for NameOnly {
116 fn write_str(&mut self, s: &str) -> std::fmt::Result {
117 if self.done {
118 return Ok(());
119 }
120 for c in s.chars() {
121 if c.is_alphanumeric() || c == '_' {
122 self.out.push(c);
123 } else {
124 self.done = true;
125 return Ok(());
126 }
127 }
128 Ok(())
129 }
130 }
131
132 let mut capture = NameOnly { out: String::new(), done: false };
133 let _ = std::fmt::write(&mut capture, format_args!("{:?}", cmd));
134 capture.out
135}
136
137fn derive_new_pack_name(existing_keys: &BTreeMap<String, Pack>) -> String {
140 let base = format!("{}{}", DEFAULT_PACK_STEM, DEFAULT_PACK_EXT);
141 if !existing_keys.contains_key(&base) {
142 return base;
143 }
144
145 let mut suffix = 2;
146 loop {
147 let candidate = format!("{}_{}{}", DEFAULT_PACK_STEM, suffix, DEFAULT_PACK_EXT);
148 if !existing_keys.contains_key(&candidate) {
149 return candidate;
150 }
151 suffix += 1;
152 }
153}
154
155fn pack_key_from_path(path: &std::path::Path) -> String {
157 path.to_string_lossy().to_string()
158}
159
160fn clipboard_entries_from_paths(pack: &Pack, paths: &[ContainerPath], pack_key: &str) -> Vec<(String, String, String)> {
168 let mut result = Vec::new();
169 for path in paths {
170 let base_path = match path.path_raw().rfind('/') {
171 Some(pos) => path.path_raw()[..pos].to_string(),
172 None => String::new(),
173 };
174 for file in pack.files_by_paths(from_ref(path), false) {
175 result.push((file.path_in_container_raw().to_string(), base_path.clone(), pack_key.to_string()));
176 }
177 }
178 result
179}
180
181fn get_pack<'a>(packs: &'a BTreeMap<String, Pack>, pack_key: &str, sender: &UnboundedSender<Response>) -> Option<&'a Pack> {
183 match packs.get(pack_key) {
184 Some(pack) => Some(pack),
185 None => {
186 CentralCommand::send_back(sender, Response::Error(format!("Pack not found: {}", pack_key)));
187 None
188 }
189 }
190}
191
192pub async fn background_loop(mut receiver: UnboundedReceiver<(UnboundedSender<Response>, Command)>, session: Arc<Session>) {
207
208 let supported_games = SupportedGames::default();
213 let mut game = supported_games.game(KEY_WARHAMMER_3).unwrap();
214 let mut schema = None;
215 let mut first_game_change_done = false;
216
217 let mut packs: BTreeMap<String, Pack> = BTreeMap::new();
219
220 let mut pack_modes: BTreeMap<String, OperationalMode> = BTreeMap::new();
222
223 let mut clipboard_entries: Vec<(String, String, String)> = Vec::new(); let mut clipboard_is_cut: bool = false;
226
227 let mut dependencies = Arc::new(RwLock::new(Dependencies::default()));
229
230 let _ = init_config_path();
232 let mut settings = Settings::init(false).unwrap();
233 let mut backup_settings = settings.clone();
234
235 rpfm_telemetry::set_usage_telemetry_enabled(settings.bool(ENABLE_USAGE_TELEMETRY));
237 rpfm_telemetry::set_crash_reports_enabled(settings.bool(ENABLE_CRASH_REPORTS));
238
239 info!("Background Thread looping around…");
246 'background_loop: while let Some((sender, response)) = receiver.recv().await {
247
248 match &response {
251 Command::Exit | Command::ClientDisconnecting => {}
252 cmd => rpfm_telemetry::record_action(&command_name(cmd)),
253 }
254
255 match response {
256
257 Command::Exit => break,
259
260 Command::ClientDisconnecting => {
263 CentralCommand::send_back(&sender, Response::Success);
264 }
265
266 Command::CheckUpdates => {
268 let sender = sender.clone();
269 let settings = settings.clone();
270 tokio::spawn(async move {
271 let result = tokio::task::spawn_blocking(move || {
272 updater::check_updates_rpfm(&settings)
273 }).await.unwrap();
274
275 match result {
276 Ok(response) => CentralCommand::send_back(&sender, Response::APIResponse(response)),
277 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
278 }
279 });
280 }
281
282 Command::CheckSchemaUpdates => {
283 git_update_check(sender, schemas_path, SCHEMA_REPO, SCHEMA_BRANCH, SCHEMA_REMOTE);
284 }
285
286 Command::CheckLuaAutogenUpdates => {
287 git_update_check(sender, lua_autogen_base_path, LUA_REPO, LUA_BRANCH, LUA_REMOTE);
288 }
289
290 Command::CheckEmpireAndNapoleonAKUpdates => {
291 git_update_check(sender, old_ak_files_path, OLD_AK_REPO, OLD_AK_BRANCH, OLD_AK_REMOTE);
292 }
293
294 Command::CheckTranslationsUpdates => {
295 git_update_check(sender, translations_remote_path, TRANSLATIONS_REPO, TRANSLATIONS_BRANCH, TRANSLATIONS_REMOTE);
296 }
297
298 Command::ClosePack(pack_key) => {
300 if packs.remove(&pack_key).is_some() {
301 pack_modes.remove(&pack_key);
302 session.remove_pack_name(&pack_key);
303 CentralCommand::send_back(&sender, Response::Success);
304 } else {
305 CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key)));
306 }
307 }
308
309 Command::CloseAllPacks => {
310 for pack_key in packs.keys().cloned().collect::<Vec<_>>() {
311 session.remove_pack_name(&pack_key);
312 }
313 packs.clear();
314 pack_modes.clear();
315 CentralCommand::send_back(&sender, Response::Success);
316 }
317
318 Command::ListOpenPacks => {
320 let pack_list: Vec<(String, ContainerInfo)> = packs.iter()
321 .map(|(key, pack)| (key.clone(), ContainerInfo::from(pack)))
322 .collect();
323 CentralCommand::send_back(&sender, Response::VecStringContainerInfo(pack_list));
324 }
325
326 Command::NewPack => {
328 let pack_version = game.pfh_version_by_file_type(PFHFileType::Mod);
329 let key = derive_new_pack_name(&packs);
330 let mut pack = Pack::new_with_name_and_version(&key, pack_version);
331
332 if let Some(version_number) = game.game_version_number(&settings.path_buf(game.key())) {
333 pack.set_game_version(version_number);
334 }
335 session.add_pack_name(&key);
336 packs.insert(key.clone(), pack);
337 pack_modes.insert(key.clone(), OperationalMode::Normal);
338 CentralCommand::send_back(&sender, Response::String(key));
339 }
340
341 Command::OpenPackFiles(paths) => {
343 let key = if let Some(first_path) = paths.first() {
344 pack_key_from_path(first_path)
345 } else {
346 format!("{}{}", DEFAULT_PACK_STEM, DEFAULT_PACK_EXT)
347 };
348
349 if packs.contains_key(&key) {
350 CentralCommand::send_back(&sender, Response::Error(format!(
351 "Pack '{}' is already open. Close it first if you want to reopen it.", key
352 )));
353 } else {
354 match Pack::read_and_merge(&paths, game, settings.bool("use_lazy_loading"), false, false) {
355 Ok(mut pack) => {
356
357 if let Some(ref schema) = schema {
359 let mut decode_extra_data = DecodeableExtraData::default();
360 decode_extra_data.set_schema(Some(schema));
361 let extra_data = Some(decode_extra_data);
362
363 let mut files = pack.files_by_type_mut(&[FileType::DB, FileType::Loc]);
364 files.par_iter_mut().for_each(|file| {
365 let _ = file.decode(&extra_data, true, false);
366 });
367 }
368
369 session.add_pack_name(&key);
370
371 let info = ContainerInfo::from(&pack);
372 packs.insert(key.clone(), pack);
373 pack_modes.insert(key.clone(), OperationalMode::Normal);
374 CentralCommand::send_back(&sender, Response::StringContainerInfo(key, info));
375 }
376 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
377 }
378 }
379 }
380
381 Command::LoadAllCAPackFiles => {
383 let key = "CA PackFiles".to_string();
384
385 if packs.contains_key(&key) {
386 CentralCommand::send_back(&sender, Response::Error(format!(
387 "Pack '{}' is already open. Close it first if you want to reopen it.", key
388 )));
389 } else {
390 match Pack::read_and_merge_ca_packs(game, &settings.path_buf(game.key())) {
391 Ok(mut pack) => {
392
393 if let Some(ref schema) = schema {
395 let mut decode_extra_data = DecodeableExtraData::default();
396 decode_extra_data.set_schema(Some(schema));
397 let extra_data = Some(decode_extra_data);
398
399 let mut files = pack.files_by_type_mut(&[FileType::DB, FileType::Loc]);
400 files.par_iter_mut().for_each(|file| {
401 let _ = file.decode(&extra_data, true, false);
402 });
403 }
404
405 session.add_pack_name(&key);
406
407 let info = ContainerInfo::from(&pack);
408 packs.insert(key.clone(), pack);
409 pack_modes.insert(key.clone(), OperationalMode::Normal);
410 CentralCommand::send_back(&sender, Response::StringContainerInfo(key, info));
411 }
412 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
413 }
414 }
415 }
416
417 Command::SavePack(pack_key) => {
419 match packs.get_mut(&pack_key) {
420 Some(pack) => {
421 let extra_data = Some(EncodeableExtraData::new_from_game_info_and_settings(game, pack.compression_format(), settings.bool("disable_uuid_regeneration_on_db_tables")));
422
423 let pack_type = *pack.header().pfh_file_type();
424 if !settings.bool("allow_editing_of_ca_packfiles") && pack_type != PFHFileType::Mod && pack_type != PFHFileType::Movie {
425 CentralCommand::send_back(&sender, Response::Error(anyhow!("Pack cannot be saved due to being of CA-Only type. Either change the Pack Type or enable \"Allow Edition of CA Packs\" in the settings.").to_string()));
426 continue;
427 }
428
429 match pack.save(None, game, &extra_data) {
430 Ok(_) => CentralCommand::send_back(&sender, Response::ContainerInfo(From::from(&*pack))),
431 Err(error) => CentralCommand::send_back(&sender, Response::Error(anyhow!("Error while trying to save the currently open PackFile: {}", error).to_string())),
432 }
433 }
434 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
435 }
436 }
437
438 Command::SavePackAs(pack_key, path) => {
440 match packs.get_mut(&pack_key) {
441 Some(pack) => {
442 let extra_data = Some(EncodeableExtraData::new_from_game_info_and_settings(game, pack.compression_format(), settings.bool("disable_uuid_regeneration_on_db_tables")));
443
444 let pack_type = *pack.header().pfh_file_type();
445 if !settings.bool("allow_editing_of_ca_packfiles") && pack_type != PFHFileType::Mod && pack_type != PFHFileType::Movie {
446 CentralCommand::send_back(&sender, Response::Error(anyhow!("Pack cannot be saved due to being of CA-Only type. Either change the Pack Type or enable \"Allow Edition of CA Packs\" in the settings.").to_string()));
447 continue;
448 }
449
450 match pack.save(Some(&path), game, &extra_data) {
451 Ok(_) => CentralCommand::send_back(&sender, Response::ContainerInfo(From::from(&*pack))),
452 Err(error) => CentralCommand::send_back(&sender, Response::Error(anyhow!("Error while trying to save the currently open PackFile: {}", error).to_string())),
453 }
454 }
455 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
456 }
457 }
458
459 Command::CleanAndSavePackAs(pack_key, path) => {
461 match packs.get_mut(&pack_key) {
462 Some(pack) => {
463 pack.clean_undecoded();
464
465 let extra_data = Some(EncodeableExtraData::new_from_game_info_and_settings(game, pack.compression_format(), settings.bool("disable_uuid_regeneration_on_db_tables")));
466 match pack.save(Some(&path), game, &extra_data) {
467 Ok(_) => CentralCommand::send_back(&sender, Response::ContainerInfo(From::from(&*pack))),
468 Err(error) => CentralCommand::send_back(&sender, Response::Error(anyhow!("Error while trying to save the currently open PackFile: {}", error).to_string())),
469 }
470 }
471 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
472 }
473 }
474
475 Command::GetPackFileDataForTreeView(pack_key) => {
477 match packs.get(&pack_key) {
478 Some(pack) => {
479 CentralCommand::send_back(&sender, Response::ContainerInfoVecRFileInfo((
480 From::from(pack),
481 pack.files().par_iter().map(|(_, file)| From::from(file)).collect(),
482 )));
483 }
484 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
485 }
486 }
487
488 Command::GetRFileInfo(pack_key, path) => {
490 match packs.get(&pack_key) {
491 Some(pack) => {
492 CentralCommand::send_back(&sender, Response::OptionRFileInfo(
493 pack.files().get(&path).map(From::from)
494 ));
495 }
496 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
497 }
498 }
499
500 Command::GetPackedFilesInfo(pack_key, paths) => {
502 match packs.get(&pack_key) {
503 Some(pack) => {
504 let paths = paths.iter().map(|path| ContainerPath::File(path.to_owned())).collect::<Vec<_>>();
505 CentralCommand::send_back(&sender, Response::VecRFileInfo(
506 pack.files_by_paths(&paths, false).into_iter().map(From::from).collect()
507 ));
508 }
509 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
510 }
511 }
512
513 Command::GlobalSearch(_pack_key, mut global_search) => {
515 match schema {
516 Some(ref schema) => {
517 global_search.search(game, schema, &mut packs, &mut dependencies.write().unwrap(), &[]);
518 let packed_files_info = RFileInfo::info_from_global_search(&global_search, &packs);
519 CentralCommand::send_back(&sender, Response::GlobalSearchVecRFileInfo(Box::new(global_search), packed_files_info));
520 }
521 None => CentralCommand::send_back(&sender, Response::Error(anyhow!("Schema not found. Maybe you need to download it?").to_string())),
522 }
523 }
524
525 Command::GetGameSelected => CentralCommand::send_back(&sender, Response::String(game.key().to_owned())),
526 Command::SetGameSelected(game_key, rebuild_dependencies) => {
527 info!("Setting game selected.");
528 let game_changed = game.key() != game_key || !first_game_change_done;
529 game = match supported_games.game(&game_key) {
530 Some(gi) => gi,
531 None => {
532 CentralCommand::send_back(&sender, Response::Error(anyhow!("The selected game is not supported!").to_string()));
533 continue;
534 }
535 };
536
537 for pack in packs.values_mut() {
539 let current_cf = pack.compression_format();
540 if current_cf != CompressionFormat::None && !game.compression_formats_supported().contains(¤t_cf) {
541 if let Some(new_cf) = game.compression_formats_supported().first() {
542 pack.set_compression_format(*new_cf, game);
543 } else {
544 pack.set_compression_format(CompressionFormat::None, game);
545 }
546 }
547 }
548
549 load_schema(&mut schema, &mut packs, game, &settings);
558
559 if rebuild_dependencies {
560 info!("Branch 1.");
561 let pack_dependencies: Vec<_> = packs.values()
563 .flat_map(|pack| pack.dependencies().iter().map(|x| x.1.clone()))
564 .collect();
565 let game_path = settings.path_buf(game.key());
567 let secondary_path = settings.path_buf(SECONDARY_PATH);
568 let game_clone = game.clone();
569 let handle = thread::spawn(move || {
570 let file_path = dependencies_cache_path().unwrap().join(game_clone.dependencies_cache_file_name());
571 let file_path = if game_changed { Some(&*file_path) } else { None };
572 let _ = dependencies.write().unwrap().rebuild(&None, &pack_dependencies, file_path, &game_clone, &game_path, &secondary_path);
573 dependencies
574 });
575
576 dependencies = handle.join().unwrap();
578 let dependencies_info = DependenciesInfo::new(&dependencies.read().unwrap(), game.vanilla_db_table_name_logic());
579 info!("Sending dependencies info after game selected change.");
580 let cf = packs.values().next().map(|p| p.compression_format()).unwrap_or(CompressionFormat::None);
582 CentralCommand::send_back(&sender, Response::CompressionFormatDependenciesInfo(cf, Some(dependencies_info)));
583
584 dependencies.write().unwrap().decode_tables(&schema);
586 }
587
588 else {
590 info!("Branch 2.");
591 let cf = packs.values().next().map(|p| p.compression_format()).unwrap_or(CompressionFormat::None);
592 CentralCommand::send_back(&sender, Response::CompressionFormatDependenciesInfo(cf, None));
593 };
594
595 for pack in packs.values_mut() {
597 if !pack.disk_file_path().is_empty() {
598 let pfh_file_type = *pack.header().pfh_file_type();
599 pack.header_mut().set_pfh_version(game.pfh_version_by_file_type(pfh_file_type));
600
601 if let Some(version_number) = game.game_version_number(&settings.path_buf(game.key())) {
602 pack.set_game_version(version_number);
603 }
604 }
605 }
606
607 if !first_game_change_done {
608 first_game_change_done = true;
609 }
610
611 info!("Switching game selected done.");
612 }
613
614 Command::GenerateDependenciesCache => {
616 let game_path = settings.path_buf(game.key());
617 let ignore_game_files_in_ak = settings.bool("ignore_game_files_in_ak");
618 let asskit_path = settings.assembly_kit_path(game).ok();
619
620 if game_path.is_dir() {
621 match Dependencies::generate_dependencies_cache(&schema, game, &game_path, &asskit_path, ignore_game_files_in_ak) {
622 Ok(mut cache) => {
623 let dependencies_path = dependencies_cache_path().unwrap().join(game.dependencies_cache_file_name());
624 match cache.save(&dependencies_path) {
625 Ok(_) => {
626 let secondary_path = settings.path_buf(SECONDARY_PATH);
627 let pack_dependencies: Vec<_> = packs.values()
628 .flat_map(|pack| pack.dependencies().iter().map(|x| x.1.clone()))
629 .collect();
630 let _ = dependencies.write().unwrap().rebuild(&schema, &pack_dependencies, Some(&dependencies_path), game, &game_path, &secondary_path);
631 let dependencies_info = DependenciesInfo::new(&dependencies.read().unwrap(), game.vanilla_db_table_name_logic());
632 CentralCommand::send_back(&sender, Response::DependenciesInfo(dependencies_info));
633 },
634 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
635 }
636 }
637 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
638 }
639 } else {
640 CentralCommand::send_back(&sender, Response::Error(anyhow!("Game Path not configured. Go to <i>'PackFile/Settings'</i> and configure it.").to_string()));
641 }
642 }
643
644 Command::UpdateCurrentSchemaFromAssKit => {
646 let ignore_game_files_in_ak = settings.bool("ignore_game_files_in_ak");
647
648 if let Some(ref mut schema) = schema {
649 match settings.assembly_kit_path(game) {
650 Ok(asskit_path) => {
651 let schema_path = schemas_path().unwrap().join(game.schema_file_name());
652
653 let dependencies = dependencies.read().unwrap();
654 if let Ok(mut tables_to_check) = dependencies.db_and_loc_data(true, false, true, false) {
655
656 for pack in packs.values() {
658 if !pack.disk_file_path().is_empty() {
659 tables_to_check.append(&mut pack.files_by_type(&[FileType::DB]));
660 }
661 }
662
663 let mut tables_to_check_split: HashMap<String, Vec<DB>> = HashMap::new();
665 for table_to_check in tables_to_check {
666 if let Ok(RFileDecoded::DB(table)) = table_to_check.decoded() {
667 match tables_to_check_split.get_mut(table.table_name()) {
668 Some(tables) => {
669
670 match tables.iter_mut().find(|x| x.definition().version() == table.definition().version()) {
672 Some(db_source) => *db_source = DB::merge(&[db_source, table]).unwrap(),
673 None => tables.push((table.clone()).clone()),
674 }
675 }
676 None => {
677 tables_to_check_split.insert(table.table_name().to_owned(), vec![table.clone()]);
678 }
679 }
680 }
681 }
682
683 let tables_to_skip = if ignore_game_files_in_ak {
684 dependencies.vanilla_loose_tables().keys().chain(dependencies.vanilla_tables().keys()).map(|x| &**x).collect::<Vec<_>>()
685 } else {
686 vec![]
687 };
688
689 match update_schema_from_raw_files(schema, game, &asskit_path, &schema_path, &tables_to_skip, &tables_to_check_split) {
690 Ok(possible_loc_fields) => {
691
692 let local_packs = if packs.is_empty() { None } else { Some(&packs) };
696 if dependencies.bruteforce_loc_key_order(schema, possible_loc_fields, local_packs, None).is_ok() {
697
698 let _ = update_schema_from_raw_files(schema, game, &asskit_path, &schema_path, &tables_to_skip, &tables_to_check_split);
700
701 if dependencies.generate_automatic_patches(schema, &packs).is_ok() {
703
704 schema.definitions_mut().par_iter_mut().for_each(|x| {
706 x.1.iter_mut().for_each(|y| {
707 y.fields_mut().iter_mut().for_each(|z| {
708 if let Some(path) = z.filename_relative_path(None) {
709 if path.len() == 1 && path[0].contains(",") {
710 let new_paths = path[0].split(',').map(|x| x.trim()).join(";");
711 z.set_filename_relative_path(Some(new_paths));
712 }
713 }
714 });
715 });
716 });
717
718 match schema.save(&schemas_path().unwrap().join(game.schema_file_name())) {
719 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
720 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
721 }
722 } else {
723 CentralCommand::send_back(&sender, Response::Success)
724 }
725 } else {
726 CentralCommand::send_back(&sender, Response::Success)
727 }
728 },
729 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
730 }
731 }
732 }
733 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
734 }
735 } else {
736 CentralCommand::send_back(&sender, Response::Error(anyhow!("There is no Schema for the Game Selected.").to_string()));
737 }
738 }
739
740 Command::OptimizePackFile(pack_key, options) => {
742 match packs.get_mut(&pack_key) {
743 Some(pack) => {
744 if let Some(ref schema) = schema {
745 match pack.optimize(None, &mut dependencies.write().unwrap(), schema, game, &options) {
746 Ok((paths_to_delete, paths_to_add)) => CentralCommand::send_back(&sender, Response::HashSetStringHashSetString(paths_to_delete, paths_to_add)),
747 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
748 }
749 } else {
750 CentralCommand::send_back(&sender, Response::Error(anyhow!("There is no Schema for the Game Selected.").to_string()));
751 }
752 }
753 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
754 }
755 }
756
757 Command::PatchSiegeAI(pack_key) => {
759 match packs.get_mut(&pack_key) {
760 Some(pack) => {
761 match pack.patch_siege_ai() {
762 Ok(result) => CentralCommand::send_back(&sender, Response::StringVecContainerPath(result.0, result.1)),
763 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string()))
764 }
765 }
766 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
767 }
768 }
769
770 Command::SetPackFileType(pack_key, new_type) => {
772 match packs.get_mut(&pack_key) {
773 Some(pack) => {
774 pack.set_pfh_file_type(new_type);
775 CentralCommand::send_back(&sender, Response::Success);
776 }
777 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
778 }
779 }
780
781 Command::ChangeIndexIncludesTimestamp(pack_key, state) => {
783 match packs.get_mut(&pack_key) {
784 Some(pack) => {
785 let mut bitmask = pack.bitmask();
786 bitmask.set(PFHFlags::HAS_INDEX_WITH_TIMESTAMPS, state);
787 pack.set_bitmask(bitmask);
788 CentralCommand::send_back(&sender, Response::Success);
789 }
790 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
791 }
792 },
793
794 Command::ChangeCompressionFormat(pack_key, cf) => {
796 match packs.get_mut(&pack_key) {
797 Some(pack) => {
798 CentralCommand::send_back(&sender, Response::CompressionFormat(pack.set_compression_format(cf, game)));
799 }
800 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
801 }
802 },
803
804 Command::GetPackFilePath(pack_key) => {
806 match packs.get(&pack_key) {
807 Some(pack) => CentralCommand::send_back(&sender, Response::PathBuf(PathBuf::from(pack.disk_file_path()))),
808 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
809 }
810 },
811
812 Command::GetDependencyPackFilesList(pack_key) => {
814 match packs.get(&pack_key) {
815 Some(pack) => CentralCommand::send_back(&sender, Response::VecBoolString(pack.dependencies().to_vec())),
816 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
817 }
818 },
819
820 Command::SetDependencyPackFilesList(pack_key, dep_packs) => {
822 match packs.get_mut(&pack_key) {
823 Some(pack) => {
824 pack.set_dependencies(dep_packs);
825 CentralCommand::send_back(&sender, Response::Success);
826 }
827 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
828 }
829 },
830
831 Command::IsThereADependencyDatabase(include_asskit) => {
833 let are_dependencies_loaded = dependencies.read().unwrap().is_vanilla_data_loaded(include_asskit);
834 CentralCommand::send_back(&sender, Response::Bool(are_dependencies_loaded))
835 },
836
837 Command::NewPackedFile(pack_key, path, new_packed_file) => {
839 let decoded = match new_packed_file {
840 NewFile::AnimPack(_) => {
841 let file = AnimPack::default();
842 RFileDecoded::AnimPack(file)
843 },
844 NewFile::DB(_, table, version) => {
845 if let Some(ref schema) = schema {
846 match schema.definition_by_name_and_version(&table, version) {
847 Some(definition) => {
848 let patches = schema.patches_for_table(&table);
849 let file = DB::new(definition, patches, &table);
850 RFileDecoded::DB(file)
851 }
852 None => {
853 CentralCommand::send_back(&sender, Response::Error(format!("No definitions found for the table `{}`, version `{}` in the currently loaded schema.", table, version)));
854 continue;
855 }
856 }
857 } else {
858 CentralCommand::send_back(&sender, Response::Error("There is no Schema for the Game Selected.".to_string()));
859 continue;
860 }
861 },
862 NewFile::Loc(_) => {
863 let file = Loc::new();
864 RFileDecoded::Loc(file)
865 }
866 NewFile::PortraitSettings(_, version, entries) => {
867 let mut file = PortraitSettings::default();
868 file.set_version(version);
869
870 if !entries.is_empty() {
871
872 let mut dependencies = dependencies.write().unwrap();
873 let mut vanilla_files = dependencies.files_by_types_mut(&[FileType::PortraitSettings], true, true);
874 let vanilla_files_decoded = vanilla_files.iter_mut()
875 .filter_map(|(_, file)| file.decode(&None, false, true).ok().flatten())
876 .filter_map(|file| if let RFileDecoded::PortraitSettings(file) = file { Some(file) } else { None })
877 .collect::<Vec<_>>();
878
879 let vanilla_values = vanilla_files_decoded.iter()
880 .flat_map(|file| file.entries())
881 .map(|entry| (entry.id(), entry))
882 .collect::<HashMap<_,_>>();
883
884 for (from_id, to_id) in entries {
885 if let Some(from_entry) = vanilla_values.get(&from_id) {
886 let mut new_entry = (*from_entry).clone();
887 new_entry.set_id(to_id);
888 file.entries_mut().push(new_entry);
889 }
890 }
891 }
892
893 RFileDecoded::PortraitSettings(file)
894 },
895 NewFile::Text(_, text_type) => {
896 let mut file = Text::default();
897 file.set_format(text_type);
898 RFileDecoded::Text(file)
899 },
900
901 NewFile::VMD(_) => {
902 let mut file = Text::default();
903 file.set_format(TextFormat::Xml);
904 RFileDecoded::VMD(file)
905 },
906
907 NewFile::WSModel(_) => {
908 let mut file = Text::default();
909 file.set_format(TextFormat::Xml);
910 RFileDecoded::WSModel(file)
911 },
912 };
913 let file = RFile::new_from_decoded(&decoded, 0, &path);
914 match packs.get_mut(&pack_key) {
915 Some(pack) => {
916 match pack.insert(file) {
917 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
918 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
919 }
920 }
921 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
922 }
923 }
924
925 Command::AddPackedFiles(pack_key, source_paths, destination_paths, paths_to_ignore) => {
927 match packs.get_mut(&pack_key) {
928 Some(pack) => {
929 let mut added_paths = vec![];
930 let mut it_broke = None;
931
932 let paths = source_paths.iter().zip(destination_paths.iter()).collect::<Vec<(&PathBuf, &ContainerPath)>>();
933 for (source_path, destination_path) in paths {
934
935 if let Some(ref paths_to_ignore) = paths_to_ignore {
937 if paths_to_ignore.iter().any(|x| source_path.starts_with(x)) {
938 continue;
939 }
940 }
941
942 match destination_path {
943 ContainerPath::File(destination_path) => {
944 match pack.insert_file(source_path, destination_path, &schema) {
945 Ok(path) => if let Some(path) = path {
946 added_paths.push(path);
947 },
948 Err(error) => it_broke = Some(error),
949 }
950 },
951
952 ContainerPath::Folder(destination_path) => {
954 match pack.insert_folder(source_path, destination_path, &None, &schema, settings.bool("include_base_folder_on_add_from_folder")) {
955 Ok(mut paths) => added_paths.append(&mut paths),
956 Err(error) => it_broke = Some(error),
957 }
958 },
959 }
960 }
961
962 CentralCommand::send_back(&sender, Response::VecContainerPathOptionString(added_paths.to_vec(), it_broke.map(|e| e.to_string())));
963
964 if let Some(ref schema) = schema {
966 let mut decode_extra_data = DecodeableExtraData::default();
967 decode_extra_data.set_schema(Some(schema));
968 let extra_data = Some(decode_extra_data);
969
970 let mut files = pack.files_by_paths_mut(&added_paths, false);
971 files.par_iter_mut()
972 .filter(|file| file.file_type() == FileType::DB || file.file_type() == FileType::Loc)
973 .for_each(|file| {
974 let _ = file.decode(&extra_data, true, false);
975 }
976 );
977 }
978 }
979 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
980 }
981 }
982
983 Command::AddPackedFilesFromPackFile(target_key, source_key, paths) => {
985 let files = match packs.get(&source_key) {
987 Some(source_pack) => {
988 source_pack.files_by_paths(&paths, false)
989 .into_iter()
990 .cloned()
991 .collect::<Vec<RFile>>()
992 }
993 None => {
994 CentralCommand::send_back(&sender, Response::Error(format!("Source pack not found: {}", source_key)));
995 continue;
996 }
997 };
998
999 match packs.get_mut(&target_key) {
1001 Some(target_pack) => {
1002 let mut added_paths = Vec::with_capacity(files.len());
1003 for file in files {
1004 if let Ok(Some(path)) = target_pack.insert(file) {
1005 added_paths.push(path);
1006 }
1007 }
1008
1009 CentralCommand::send_back(&sender, Response::VecContainerPath(added_paths.to_vec()));
1010
1011 if let Some(ref schema) = schema {
1013 let mut decode_extra_data = DecodeableExtraData::default();
1014 decode_extra_data.set_schema(Some(schema));
1015 let extra_data = Some(decode_extra_data);
1016
1017 let mut files = target_pack.files_by_paths_mut(&added_paths, false);
1018 files.par_iter_mut()
1019 .filter(|file| file.file_type() == FileType::DB || file.file_type() == FileType::Loc)
1020 .for_each(|file| {
1021 let _ = file.decode(&extra_data, true, false);
1022 }
1023 );
1024 }
1025 }
1026 None => CentralCommand::send_back(&sender, Response::Error(format!("Target pack not found: {}", target_key))),
1027 }
1028 }
1029
1030 Command::AddPackedFilesFromPackFileToAnimpack(pack_key, anim_pack_path, paths) => {
1032 match packs.get_mut(&pack_key) {
1033 Some(pack) => {
1034 let files = pack.files_by_paths(&paths, false)
1035 .into_iter()
1036 .map(|file| {
1037 let mut file = file.clone();
1038 let _ = file.load();
1039 file
1040 })
1041 .collect::<Vec<RFile>>();
1042
1043 match pack.files_mut().get_mut(&anim_pack_path) {
1044 Some(file) => {
1045
1046 let extra_data = DecodeableExtraData::default();
1048 let _ = file.decode(&Some(extra_data), true, false);
1050
1051 match file.decoded_mut() {
1052 Ok(decoded) => match decoded {
1053 RFileDecoded::AnimPack(anim_pack) => {
1054 let mut paths = Vec::with_capacity(files.len());
1055 for file in files {
1056 if let Ok(Some(path)) = anim_pack.insert(file) {
1057 paths.push(path);
1058 }
1059 }
1060
1061 CentralCommand::send_back(&sender, Response::VecContainerPath(paths.to_vec()));
1062 }
1063 _ => CentralCommand::send_back(&sender, Response::Error(format!("We expected {} to be of type {} but found {}. This is either a bug or you did weird things with the game selected.", anim_pack_path, FileType::AnimPack, FileType::from(&*decoded)))),
1064 }
1065 _ => CentralCommand::send_back(&sender, Response::Error(format!("Failed to decode the file at the following path: {}", anim_pack_path))),
1066 }
1067 }
1068 None => CentralCommand::send_back(&sender, Response::Error(format!("File not found in the Pack: {}.", anim_pack_path))),
1069 }
1070 }
1071 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
1072 }
1073 }
1074
1075 Command::AddPackedFilesFromAnimpack(pack_key, data_source, anim_pack_path, paths) => {
1077 let mut dependencies = dependencies.write().unwrap();
1078 let anim_pack_file = match data_source {
1079 DataSource::PackFile => packs.get_mut(&pack_key).and_then(|pack| pack.files_mut().get_mut(&anim_pack_path)),
1080 DataSource::GameFiles => dependencies.file_mut(&anim_pack_path, true, false).ok(),
1081 DataSource::ParentFiles => dependencies.file_mut(&anim_pack_path, false, true).ok(),
1082 DataSource::AssKitFiles |
1083 DataSource::ExternalFile => unreachable!("add_files_to_animpack"),
1084 };
1085
1086 let files = match anim_pack_file {
1087 Some(file) => {
1088
1089 let extra_data = DecodeableExtraData::default();
1091 let _ = file.decode(&Some(extra_data), true, false);
1093
1094 match file.decoded_mut() {
1095 Ok(decoded) => match decoded {
1096 RFileDecoded::AnimPack(anim_pack) => anim_pack.files_by_paths(&paths, false).into_iter().cloned().collect::<Vec<RFile>>(),
1097 _ => {
1098 CentralCommand::send_back(&sender, Response::Error(format!("We expected {} to be of type {} but found {}. This is either a bug or you did weird things with the game selected.", anim_pack_path, FileType::AnimPack, FileType::from(&*decoded))));
1099 continue;
1100 },
1101 }
1102 _ => {
1103 CentralCommand::send_back(&sender, Response::Error(format!("Failed to decode the file at the following path: {}", anim_pack_path)));
1104 continue;
1105 },
1106 }
1107 }
1108 None => {
1109 CentralCommand::send_back(&sender, Response::Error(format!("The file with the path {} doesn't exists on the open Pack.", anim_pack_path)));
1110 continue;
1111 }
1112 };
1113
1114 let result_paths = files.iter().map(|file| file.path_in_container()).collect::<Vec<_>>();
1115 match packs.get_mut(&pack_key) {
1116 Some(pack) => {
1117 for mut file in files {
1118 let _ = file.guess_file_type();
1119 let _ = pack.insert(file);
1120 }
1121 CentralCommand::send_back(&sender, Response::VecContainerPath(result_paths));
1122 }
1123 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
1124 }
1125 }
1126
1127 Command::DeleteFromAnimpack(pack_key, anim_pack_path, paths) => {
1129 match packs.get_mut(&pack_key) {
1130 Some(pack) => {
1131 match pack.files_mut().get_mut(&anim_pack_path) {
1132 Some(file) => {
1133
1134 let extra_data = DecodeableExtraData::default();
1136 let _ = file.decode(&Some(extra_data), true, false);
1138
1139 match file.decoded_mut() {
1140 Ok(decoded) => match decoded {
1141 RFileDecoded::AnimPack(anim_pack) => {
1142 for path in paths {
1143 anim_pack.remove(&path);
1144 }
1145
1146 CentralCommand::send_back(&sender, Response::Success);
1147 }
1148 _ => CentralCommand::send_back(&sender, Response::Error(format!("We expected {} to be of type {} but found {}. This is either a bug or you did weird things with the game selected.", anim_pack_path, FileType::AnimPack, FileType::from(&*decoded)))),
1149 }
1150 _ => CentralCommand::send_back(&sender, Response::Error(format!("Failed to decode the file at the following path: {}", anim_pack_path))),
1151 }
1152 }
1153 None => CentralCommand::send_back(&sender, Response::Error(format!("File not found in the Pack: {}.", anim_pack_path))),
1154 }
1155 }
1156 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
1157 }
1158 }
1159
1160 Command::DecodePackedFile(pack_key, path, data_source) => {
1162 info!("Trying to decode a file. Path: {}", &path);
1163 info!("Trying to decode a file. Data Source: {}", &data_source);
1164
1165 match data_source {
1166 DataSource::PackFile => {
1167 match packs.get_mut(&pack_key) {
1168 Some(pack) => {
1169 if path == RESERVED_NAME_NOTES {
1170 let mut note = Text::default();
1171 note.set_format(TextFormat::Markdown);
1172 note.set_contents(pack.notes().pack_notes().to_owned());
1173 CentralCommand::send_back(&sender, Response::Text(note));
1174 }
1175
1176 else {
1177
1178 match pack.files_mut().get_mut(&path) {
1180 Some(file) => decode_and_send_file(file, &sender, &settings, game, &schema),
1181 None => CentralCommand::send_back(&sender, Response::Error(format!("The file with the path {} hasn't been found on this Pack.", path))),
1182 }
1183 }
1184 }
1185 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
1186 }
1187 }
1188
1189 DataSource::ParentFiles => {
1190 match dependencies.write().unwrap().file_mut(&path, false, true) {
1191 Ok(file) => decode_and_send_file(file, &sender, &settings, game, &schema),
1192 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
1193 }
1194 }
1195
1196 DataSource::GameFiles => {
1197 match dependencies.write().unwrap().file_mut(&path, true, false) {
1198 Ok(file) => decode_and_send_file(file, &sender, &settings, game, &schema),
1199 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
1200 }
1201 }
1202
1203 DataSource::AssKitFiles => {
1204 let path_split = path.split('/').collect::<Vec<_>>();
1205 if path_split.len() > 2 {
1206 match dependencies.read().unwrap().asskit_only_db_tables().get(path_split[1]) {
1207 Some(db) => CentralCommand::send_back(&sender, Response::DBRFileInfo(db.clone(), RFileInfo::default())),
1208 None => CentralCommand::send_back(&sender, Response::Error(format!("Table {} not found on Assembly Kit files.", path))),
1209 }
1210 } else {
1211 CentralCommand::send_back(&sender, Response::Error(format!("Path {} doesn't contain an identifiable table name.", path)));
1212 }
1213 }
1214
1215 DataSource::ExternalFile => {
1216 CentralCommand::send_back(&sender, Response::Success);
1217 }
1218 }
1219 }
1220
1221 Command::SavePackedFileFromView(pack_key, path, file_decoded) => {
1223 match packs.get_mut(&pack_key) {
1224 Some(pack) => {
1225 if path == RESERVED_NAME_NOTES {
1226 if let RFileDecoded::Text(data) = file_decoded {
1227 pack.notes_mut().set_pack_notes(data.contents().to_owned());
1228 }
1229 }
1230 else if let Some(file) = pack.files_mut().get_mut(&path) {
1231 if let Err(error) = file.set_decoded(file_decoded) {
1232 CentralCommand::send_back(&sender, Response::Error(error.to_string()));
1233 continue;
1234 }
1235 }
1236 CentralCommand::send_back(&sender, Response::Success);
1237 }
1238 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
1239 }
1240 }
1241
1242 Command::DeletePackedFiles(pack_key, paths) => {
1244 match packs.get_mut(&pack_key) {
1245 Some(pack) => CentralCommand::send_back(&sender, Response::VecContainerPath(paths.iter().flat_map(|path| pack.remove(path)).collect())),
1246 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
1247 }
1248 }
1249
1250 Command::CopyPackedFiles(paths_by_pack) => {
1252 clipboard_entries.clear();
1253 for (pack_key, paths) in &paths_by_pack {
1254 if let Some(pack) = packs.get(pack_key) {
1255 clipboard_entries.extend(clipboard_entries_from_paths(pack, paths, pack_key));
1256 }
1257 }
1258 clipboard_is_cut = false;
1259 CentralCommand::send_back(&sender, Response::Success);
1260 }
1261
1262 Command::CutPackedFiles(paths_by_pack) => {
1264 clipboard_entries.clear();
1265 for (pack_key, paths) in &paths_by_pack {
1266 if let Some(pack) = packs.get(pack_key) {
1267 clipboard_entries.extend(clipboard_entries_from_paths(pack, paths, pack_key));
1268 }
1269 }
1270 clipboard_is_cut = true;
1271 CentralCommand::send_back(&sender, Response::Success);
1272 }
1273
1274 Command::PastePackedFiles(target_key, destination_path) => {
1276 if clipboard_entries.is_empty() {
1277 CentralCommand::send_back(&sender, Response::Error("Clipboard is empty.".to_string()));
1278 } else {
1279
1280 let mut files_to_insert: Vec<RFile> = Vec::with_capacity(clipboard_entries.len());
1283 for (file_path, base_path, source_key) in &clipboard_entries {
1284 if let Some(source_pack) = packs.get(source_key) {
1285 let path_as_container = ContainerPath::File(file_path.clone());
1286 let found = source_pack.files_by_paths(&[path_as_container], false);
1287 if let Some(file) = found.first() {
1288 let mut new_file = (*file).clone();
1289
1290 let relative_path = if !base_path.is_empty() && file_path.starts_with(base_path) {
1292 file_path[base_path.len()..].trim_start_matches('/')
1293 } else {
1294 file_path
1295 };
1296 let new_path = if destination_path.is_empty() {
1297 relative_path.to_string()
1298 } else {
1299 format!("{}/{}", destination_path.trim_end_matches('/'), relative_path)
1300 };
1301 new_file.set_path_in_container_raw(&new_path);
1302 files_to_insert.push(new_file);
1303 }
1304 }
1305 }
1306
1307 let mut cut_deleted_by_pack: BTreeMap<String, Vec<ContainerPath>> = BTreeMap::new();
1309 if clipboard_is_cut {
1310 for (file_path, _, source_key) in &clipboard_entries {
1311 if let Some(source_pack) = packs.get_mut(source_key) {
1312 let removed = source_pack.remove(&ContainerPath::File(file_path.clone()));
1313 cut_deleted_by_pack.entry(source_key.clone()).or_default().extend(removed);
1314 }
1315 }
1316 }
1317
1318 match packs.get_mut(&target_key) {
1320 Some(target_pack) => {
1321 let mut added_paths = Vec::with_capacity(files_to_insert.len());
1322 for new_file in files_to_insert {
1323 if let Ok(Some(path)) = target_pack.insert(new_file) {
1324 added_paths.push(path);
1325 }
1326 }
1327
1328 if let Some(ref schema) = schema {
1330 let mut decode_extra_data = DecodeableExtraData::default();
1331 decode_extra_data.set_schema(Some(schema));
1332 let extra_data = Some(decode_extra_data);
1333
1334 let mut files = target_pack.files_by_paths_mut(&added_paths, false);
1335 files.par_iter_mut()
1336 .filter(|file| file.file_type() == FileType::DB || file.file_type() == FileType::Loc)
1337 .for_each(|file| {
1338 let _ = file.decode(&extra_data, true, false);
1339 });
1340 }
1341
1342 CentralCommand::send_back(&sender, Response::VecContainerPathBTreeMapStringVecContainerPath(added_paths, cut_deleted_by_pack));
1343
1344 if clipboard_is_cut {
1346 clipboard_entries.clear();
1347 clipboard_is_cut = false;
1348 }
1349 }
1350 None => CentralCommand::send_back(&sender, Response::Error(format!("Target pack not found: {}", target_key))),
1351 }
1352 }
1353 }
1354
1355 Command::DuplicatePackedFiles(pack_key, paths) => {
1357 match packs.get_mut(&pack_key) {
1358 Some(pack) => {
1359 let files_to_dup: Vec<RFile> = pack.files_by_paths(&paths, false)
1361 .into_iter()
1362 .cloned()
1363 .collect();
1364
1365 let mut added_paths = Vec::with_capacity(files_to_dup.len());
1366 for file in files_to_dup {
1367 let old_path = file.path_in_container_raw().to_string();
1368
1369 let new_path = if let Some(dot_pos) = old_path.rfind('.') {
1371 let (base, ext) = old_path.split_at(dot_pos);
1372
1373 let base_trimmed = base.trim_end_matches(|c: char| c.is_ascii_digit());
1375 let suffix_str = &base[base_trimmed.len()..];
1376 let mut counter = suffix_str.parse::<u32>().unwrap_or(0) + 1;
1377
1378 loop {
1380 let candidate = format!("{}{}{}", base_trimmed, counter, ext);
1381 if !pack.has_file(&candidate) {
1382 break candidate;
1383 }
1384 counter += 1;
1385 }
1386 } else {
1387 let base_trimmed = old_path.trim_end_matches(|c: char| c.is_ascii_digit());
1389 let suffix_str = &old_path[base_trimmed.len()..];
1390 let mut counter = suffix_str.parse::<u32>().unwrap_or(0) + 1;
1391
1392 loop {
1393 let candidate = format!("{}{}", base_trimmed, counter);
1394 if !pack.has_file(&candidate) {
1395 break candidate;
1396 }
1397 counter += 1;
1398 }
1399 };
1400
1401 let mut new_file = file;
1402 new_file.set_path_in_container_raw(&new_path);
1403
1404 if let Ok(Some(path)) = pack.insert(new_file) {
1405 added_paths.push(path);
1406 }
1407 }
1408
1409 if let Some(ref schema) = schema {
1411 let mut decode_extra_data = DecodeableExtraData::default();
1412 decode_extra_data.set_schema(Some(schema));
1413 let extra_data = Some(decode_extra_data);
1414
1415 let mut files = pack.files_by_paths_mut(&added_paths, false);
1416 files.par_iter_mut()
1417 .filter(|file| file.file_type() == FileType::DB || file.file_type() == FileType::Loc)
1418 .for_each(|file| {
1419 let _ = file.decode(&extra_data, true, false);
1420 });
1421 }
1422
1423 CentralCommand::send_back(&sender, Response::VecContainerPath(added_paths));
1424 }
1425 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
1426 }
1427 }
1428
1429 Command::ExtractPackedFiles(pack_key, container_paths, path, extract_tables_to_tsv) => {
1431 let schema = if extract_tables_to_tsv { &schema } else { &None };
1432 let mut errors = 0;
1433
1434 if let Some(container_paths) = container_paths.get(&DataSource::PackFile) {
1436 match packs.get_mut(&pack_key) {
1437 Some(pack) => {
1438 let extra_data = Some(EncodeableExtraData::new_from_game_info_and_settings(game, pack.compression_format(), settings.bool("disable_uuid_regeneration_on_db_tables")));
1439 let mut extracted_paths = vec![];
1440
1441 for container_path in container_paths {
1442 match pack.extract(container_path.clone(), &path, true, schema, false, settings.bool("tables_use_old_column_order_for_tsv"), &extra_data) {
1443 Ok(mut extracted_path) => extracted_paths.append(&mut extracted_path),
1444 Err(_) => {
1445 errors += 1;
1447 },
1448 }
1449 }
1450
1451 if errors == 0 {
1452 CentralCommand::send_back(&sender, Response::StringVecPathBuf(tr("files_extracted_success"), extracted_paths));
1453 } else {
1454 CentralCommand::send_back(&sender, Response::Error(format!("There were {} errors while extracting.", errors)));
1455 }
1456 }
1457 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
1458 }
1459 }
1460
1461 else {
1463
1464 let dependencies = dependencies.read().unwrap();
1465 let mut game_files = if let Some(container_paths) = container_paths.get(&DataSource::GameFiles) {
1466 dependencies.files_by_path(container_paths, true, false, false)
1467 } else {
1468 HashMap::new()
1469 };
1470 let parent_files = if let Some(container_paths) = container_paths.get(&DataSource::ParentFiles) {
1471 dependencies.files_by_path(container_paths, false, true, false)
1472 } else {
1473 HashMap::new()
1474 };
1475
1476 game_files.extend(parent_files);
1477
1478 let mut pack = Pack::default();
1479 let extra_data = Some(EncodeableExtraData::new_from_game_info_and_settings(game, pack.compression_format(), settings.bool("disable_uuid_regeneration_on_db_tables")));
1480 let mut extracted_paths = vec![];
1481 for (path_raw, file) in game_files {
1482 if pack.insert(file.clone()).is_err() {
1483 errors += 1;
1484 continue;
1485 }
1486
1487 let container_path = ContainerPath::File(path_raw);
1488 match pack.extract(container_path.clone(), &path, true, schema, false, settings.bool("tables_use_old_column_order_for_tsv"), &extra_data) {
1489 Ok(mut extracted_path) => extracted_paths.append(&mut extracted_path),
1490 Err(_) => errors += 1,
1491 }
1492
1493 pack.remove(&container_path);
1495 }
1496
1497 if errors == 0 {
1498 CentralCommand::send_back(&sender, Response::StringVecPathBuf(tr("files_extracted_success"), extracted_paths));
1499 } else {
1500 CentralCommand::send_back(&sender, Response::Error(format!("There were {} errors while extracting.", errors)));
1501 }
1502 }
1503 }
1504
1505 Command::RenamePackedFiles(pack_key, renaming_data) => {
1507 match packs.get_mut(&pack_key) {
1508 Some(pack) => {
1509 match pack.move_paths(&renaming_data) {
1510 Ok(data) => CentralCommand::send_back(&sender, Response::VecContainerPathContainerPath(data)),
1511 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
1512 }
1513 }
1514 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
1515 }
1516 }
1517
1518 Command::FolderExists(pack_key, path) => {
1520 match packs.get(&pack_key) {
1521 Some(pack) => CentralCommand::send_back(&sender, Response::Bool(pack.has_folder(&path))),
1522 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
1523 }
1524 }
1525
1526 Command::PackedFileExists(pack_key, path) => {
1528 match packs.get(&pack_key) {
1529 Some(pack) => CentralCommand::send_back(&sender, Response::Bool(pack.has_file(&path))),
1530 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
1531 }
1532 }
1533
1534 Command::GetTableListFromDependencyPackFile => {
1536 let dependencies = dependencies.read().unwrap();
1537 CentralCommand::send_back(&sender, Response::VecString(dependencies.vanilla_loose_tables().keys().chain(dependencies.vanilla_tables().keys()).map(|x| x.to_owned()).collect()))
1538 },
1539 Command::GetCustomTableList => match &schema {
1540 Some(schema) => {
1541 let tables = schema.definitions().par_iter().filter(|(key, defintions)|
1542 !defintions.is_empty() && (
1543 key.starts_with("start_pos_") ||
1544 key.starts_with("twad_")
1545 )
1546 ).map(|(key, _)| key.to_owned()).collect::<Vec<_>>();
1547 CentralCommand::send_back(&sender, Response::VecString(tables));
1548 }
1549 None => CentralCommand::send_back(&sender, Response::Error(anyhow!("There is no Schema for the Game Selected.").to_string()))
1550 },
1551
1552 Command::LocalArtSetIds(_pack_key) => {
1553 CentralCommand::send_back(&sender, Response::HashSetString(dependencies.read().unwrap().db_values_from_table_name_and_column_name(Some(&packs), "campaign_character_arts_tables", "art_set_id", false, false)));
1554 }
1555
1556 Command::DependenciesArtSetIds => CentralCommand::send_back(&sender, Response::HashSetString(dependencies.read().unwrap().db_values_from_table_name_and_column_name(None, "campaign_character_arts_tables", "art_set_id", true, true))),
1558
1559 Command::GetTableVersionFromDependencyPackFile(table_name) => {
1561 if dependencies.read().unwrap().is_vanilla_data_loaded(false) {
1562 match dependencies.read().unwrap().db_version(&table_name) {
1563 Some(version) => CentralCommand::send_back(&sender, Response::I32(version)),
1564 None => {
1565
1566 if table_name.starts_with("start_pos_") || table_name.starts_with("twad_") || table_name.starts_with("ceo") {
1568 match &schema {
1569 Some(schema) => {
1570 match schema.definitions_by_table_name(&table_name) {
1571 Some(definitions) => {
1572 if definitions.is_empty() {
1573 CentralCommand::send_back(&sender, Response::Error("There are no definitions for this specific table.".to_string()));
1574 } else {
1575 CentralCommand::send_back(&sender, Response::I32(*definitions.first().unwrap().version()));
1576 }
1577 }
1578 None => CentralCommand::send_back(&sender, Response::Error("There are no definitions for this specific table.".to_string())),
1579 }
1580 }
1581 None => CentralCommand::send_back(&sender, Response::Error("There is no Schema for the Game Selected.".to_string().to_string()))
1582 }
1583 } else {
1584 CentralCommand::send_back(&sender, Response::Error("Table not found in the game files.".to_string()))
1585 }
1586 },
1587 }
1588 } else { CentralCommand::send_back(&sender, Response::Error("Dependencies cache needs to be regenerated before this.".to_string().to_string())); }
1589 }
1590
1591 Command::GetTableDefinitionFromDependencyPackFile(table_name) => {
1592 if dependencies.read().unwrap().is_vanilla_data_loaded(false) {
1593 if let Some(ref schema) = schema {
1594 if let Some(version) = dependencies.read().unwrap().db_version(&table_name) {
1595 if let Some(definition) = schema.definition_by_name_and_version(&table_name, version) {
1596 CentralCommand::send_back(&sender, Response::Definition(definition.clone()));
1597 } else { CentralCommand::send_back(&sender, Response::Error(format!("No definition found for table {}.", table_name).to_string())); }
1598 } else { CentralCommand::send_back(&sender, Response::Error(format!("Table version not found in dependencies for table {}.", table_name).to_string())); }
1599 } else { CentralCommand::send_back(&sender, Response::Error("There is no Schema for the Game Selected.".to_string().to_string())); }
1600 } else { CentralCommand::send_back(&sender, Response::Error("Dependencies cache needs to be regenerated before this.".to_string().to_string())); }
1601 }
1602
1603 Command::MergeFiles(pack_key, paths, merged_path, delete_source_files) => {
1605 match packs.get_mut(&pack_key) {
1606 Some(pack) => {
1607 let files_to_merge = pack.files_by_paths(&paths, false);
1608 match RFile::merge(&files_to_merge, &merged_path) {
1609 Ok(file) => {
1610 let _ = pack.insert(file);
1611
1612 if delete_source_files {
1614 paths.iter()
1615 .filter(|path| merged_path != path.path_raw())
1616 .for_each(|path| { pack.remove(path); });
1617 }
1618
1619 CentralCommand::send_back(&sender, Response::String(merged_path.to_string()));
1620 },
1621 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
1622 }
1623 }
1624 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
1625 }
1626 }
1627
1628 Command::UpdateTable(pack_key, path) => {
1630 let path = path.path_raw();
1631 match packs.get_mut(&pack_key) {
1632 Some(pack) => {
1633 if let Some(rfile) = pack.file_mut(path, false) {
1634 if let Ok(decoded) = rfile.decoded_mut() {
1635 match dependencies.write().unwrap().update_db(decoded) {
1636 Ok((old_version, new_version, fields_deleted, fields_added)) => CentralCommand::send_back(&sender, Response::I32I32VecStringVecString(old_version, new_version, fields_deleted, fields_added)),
1637 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
1638 }
1639 } else { CentralCommand::send_back(&sender, Response::Error(anyhow!("File with the following path undecoded: {}", path).to_string())); }
1640 } else { CentralCommand::send_back(&sender, Response::Error(anyhow!("File not found in the open Pack: {}", path).to_string())); }
1641 }
1642 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
1643 }
1644 }
1645
1646 Command::GlobalSearchReplaceMatches(_pack_key, mut global_search, matches) => {
1648 if let Some(ref schema) = schema {
1649 match global_search.replace(game, schema, &mut packs, &mut dependencies.write().unwrap(), &matches) {
1650 Ok(paths) => {
1651 let files_info = paths.iter().flat_map(|path| {
1652 packs.values().flat_map(|pack| pack.files_by_path(path, false).iter().map(|file| RFileInfo::from(*file)).collect::<Vec<RFileInfo>>()).collect::<Vec<_>>()
1653 }).collect();
1654 CentralCommand::send_back(&sender, Response::GlobalSearchVecRFileInfo(Box::new(global_search), files_info));
1655 }
1656 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
1657 }
1658 } else {
1659 CentralCommand::send_back(&sender, Response::Error(anyhow!("Schema not found. Maybe you need to download it?").to_string()));
1660 }
1661 }
1662
1663 Command::GlobalSearchReplaceAll(_pack_key, mut global_search) => {
1665 if let Some(ref schema) = schema {
1666 match global_search.replace_all(game, schema, &mut packs, &mut dependencies.write().unwrap()) {
1667 Ok(paths) => {
1668 let files_info = paths.iter().flat_map(|path| {
1669 packs.values().flat_map(|pack| pack.files_by_path(path, false).iter().map(|file| RFileInfo::from(*file)).collect::<Vec<RFileInfo>>()).collect::<Vec<_>>()
1670 }).collect();
1671 CentralCommand::send_back(&sender, Response::GlobalSearchVecRFileInfo(Box::new(global_search), files_info));
1672 }
1673 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
1674 }
1675 } else {
1676 CentralCommand::send_back(&sender, Response::Error(anyhow!("Schema not found. Maybe you need to download it?").to_string()));
1677 }
1678 }
1679
1680 Command::GetReferenceDataFromDefinition(_pack_key, table_name, definition, force_local_ref_generation) => {
1682 let mut reference_data = HashMap::new();
1683
1684 if let Some(ref schema) = schema {
1686 if dependencies.read().unwrap().local_tables_references().get(&table_name).is_none() || force_local_ref_generation {
1687 dependencies.write().unwrap().generate_local_definition_references(schema, &table_name, &definition);
1688 }
1689
1690 reference_data = dependencies.read().unwrap().db_reference_data(schema, &packs, &table_name, &definition, &None);
1691 }
1692
1693 CentralCommand::send_back(&sender, Response::HashMapI32TableReferences(reference_data));
1694 }
1695
1696 Command::SetVideoFormat(pack_key, path, format) => {
1698 match packs.get_mut(&pack_key) {
1699 Some(pack) => {
1700 match pack.files_mut().get_mut(&path) {
1701 Some(ref mut rfile) => {
1702 match rfile.decoded_mut() {
1703 Ok(data) => {
1704 if let RFileDecoded::Video(ref mut data) = data {
1705 data.set_format(format);
1706 CentralCommand::send_back(&sender, Response::Success);
1707 } else {
1708 CentralCommand::send_back(&sender, Response::Error("The file is not a video.".to_string()));
1709 }
1710 }
1711 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
1712 }
1713 }
1714 None => CentralCommand::send_back(&sender, Response::Error("This Pack doesn't exists as a file in the disk.".to_string())),
1715 }
1716 }
1717 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
1718 }
1719 },
1720
1721 Command::SaveSchema(mut schema_new) => {
1723 match schema_new.save(&schemas_path().unwrap().join(game.schema_file_name())) {
1724 Ok(_) => {
1725 schema = Some(schema_new);
1726 CentralCommand::send_back(&sender, Response::Success);
1727 },
1728 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
1729 }
1730 }
1731
1732 Command::CleanCache(pack_key, paths) => {
1734 match packs.get_mut(&pack_key) {
1735 Some(pack) => {
1736 let cf = pack.compression_format();
1737 let mut files = pack.files_by_paths_mut(&paths, false);
1738 let extra_data = Some(EncodeableExtraData::new_from_game_info_and_settings(game, cf, settings.bool("disable_uuid_regeneration_on_db_tables")));
1739
1740 files.iter_mut().for_each(|file| {
1741 let _ = file.encode(&extra_data, true, true, false);
1742 });
1743 CentralCommand::send_back(&sender, Response::Success);
1744 }
1745 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
1746 }
1747 }
1748
1749 Command::ExportTSV(pack_key, internal_path, external_path, data_source) => {
1751 let mut dependencies = dependencies.write().unwrap();
1752 match &schema {
1753 Some(ref schema) => {
1754 let file = match data_source {
1755 DataSource::PackFile => packs.get_mut(&pack_key).and_then(|pack| pack.file_mut(&internal_path, false)),
1756 DataSource::ParentFiles => dependencies.file_mut(&internal_path, false, true).ok(),
1757 DataSource::GameFiles => dependencies.file_mut(&internal_path, true, false).ok(),
1758 DataSource::AssKitFiles => {
1759 CentralCommand::send_back(&sender, Response::Error("Exporting a TSV from the Assembly Kit is not yet supported.".to_string()));
1760 continue;
1761 },
1762 DataSource::ExternalFile => {
1763 CentralCommand::send_back(&sender, Response::Error("Exporting a TSV from a external file is not yet supported.".to_string()));
1764 continue;
1765 },
1766 };
1767 match file {
1768 Some(file) => match file.tsv_export_to_path(&external_path, schema, settings.bool("tables_use_old_column_order_for_tsv")) {
1769 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
1770 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
1771 }
1772 None => CentralCommand::send_back(&sender, Response::Error(format!("File with the following path not found in the Pack: {}", internal_path).to_string())),
1773 }
1774 },
1775 None => CentralCommand::send_back(&sender, Response::Error("There is no Schema for the Game Selected.".to_string().to_string())),
1776 }
1777 }
1778
1779 Command::ImportTSV(pack_key, internal_path, external_path) => {
1782 match packs.get_mut(&pack_key) {
1783 Some(pack) => {
1784 match pack.file_mut(&internal_path, false) {
1785 Some(file) => {
1786 match RFile::tsv_import_from_path(&external_path, &schema) {
1787 Ok(imported) => {
1788 let decoded = imported.decoded().unwrap();
1789 file.set_decoded(decoded.clone()).unwrap();
1790 CentralCommand::send_back(&sender, Response::RFileDecoded(decoded.clone()))
1791 },
1792 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
1793 }
1794 }
1795 None => CentralCommand::send_back(&sender, Response::Error(anyhow!("File with the following path not found in the Pack: {}", internal_path).to_string())),
1796 }
1797 }
1798 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
1799 }
1800 }
1801
1802 Command::OpenContainingFolder(pack_key) => {
1804 match packs.get(&pack_key) {
1805 Some(pack) => {
1806
1807 let mut path_str = pack.disk_file_path().to_owned();
1809
1810 if path_str.starts_with("//?/") || path_str.starts_with("\\\\?\\") {
1812 path_str = path_str[4..].to_string();
1813 }
1814
1815 let mut path = PathBuf::from(path_str);
1816 if path.exists() {
1817 path.pop();
1818 let _ = open::that(&path);
1819 CentralCommand::send_back(&sender, Response::Success);
1820 }
1821 else {
1822 CentralCommand::send_back(&sender, Response::Error("This Pack doesn't exists as a file in the disk.".to_string()));
1823 }
1824 }
1825 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
1826 }
1827 },
1828
1829 Command::OpenPackedFileInExternalProgram(pack_key, data_source, path) => {
1831 match data_source {
1832 DataSource::PackFile => {
1833 match packs.get_mut(&pack_key) {
1834 Some(pack) => {
1835 let folder = temp_dir().join(format!("rpfm_{}", pack.disk_file_name()));
1836 let cf = pack.compression_format();
1837 let extra_data = Some(EncodeableExtraData::new_from_game_info_and_settings(game, cf, settings.bool("disable_uuid_regeneration_on_db_tables")));
1838
1839 match pack.extract(path.clone(), &folder, true, &schema, false, settings.bool("tables_use_old_column_order_for_tsv"), &extra_data) {
1840 Ok(extracted_path) => {
1841 let _ = that(&extracted_path[0]);
1842 CentralCommand::send_back(&sender, Response::PathBuf(extracted_path[0].to_owned()));
1843 }
1844 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
1845 }
1846 }
1847 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
1848 }
1849 }
1850 _ => CentralCommand::send_back(&sender, Response::Error(anyhow!("Opening dependencies files in external programs is not yet supported.").to_string())),
1851 }
1852 }
1853
1854 Command::SavePackedFileFromExternalView(pack_key, path, external_path) => {
1856 match packs.get_mut(&pack_key) {
1857 Some(pack) => {
1858 match pack.file_mut(&path, false) {
1859 Some(file) => match file.encode_from_external_data(&schema, &external_path) {
1860 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
1861 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
1862 }
1863 None => CentralCommand::send_back(&sender, Response::Error(anyhow!("File not found").to_string())),
1864 }
1865 }
1866 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
1867 }
1868 }
1869
1870 Command::UpdateSchemas => {
1872
1873 let git_result = tokio::task::spawn_blocking(|| {
1875 match schemas_path() {
1876 Ok(local_path) => {
1877 let git_integration = GitIntegration::new(&local_path, SCHEMA_REPO, SCHEMA_BRANCH, SCHEMA_REMOTE);
1878 git_integration.update_repo().map(|_| ()).map_err(|e| anyhow::anyhow!(e.to_string()))
1879 },
1880 Err(error) => Err(error),
1881 }
1882 }).await.unwrap();
1883
1884 match git_result {
1886 Ok(_) => {
1887 let schema_path = schemas_path().unwrap().join(game.schema_file_name());
1888 let patches_path = table_patches_path().unwrap().join(game.schema_file_name());
1889
1890 for pack in packs.values_mut() {
1892 let cf = pack.compression_format();
1893 let mut tables = pack.files_by_type_mut(&[FileType::DB]);
1894 let extra_data = Some(EncodeableExtraData::new_from_game_info_and_settings(game, cf, settings.bool("disable_uuid_regeneration_on_db_tables")));
1895
1896 tables.par_iter_mut().for_each(|x| { let _ = x.encode(&extra_data, true, true, false); });
1897 }
1898
1899 schema = Schema::load(&schema_path, Some(&patches_path)).ok();
1900
1901 for pack in packs.values_mut() {
1902 let mut extra_data = DecodeableExtraData::default();
1903 extra_data.set_schema(schema.as_ref());
1904 let extra_data = Some(extra_data);
1905
1906 let mut tables = pack.files_by_type_mut(&[FileType::DB]);
1907 tables.par_iter_mut().for_each(|x| {
1908 let _ = x.decode(&extra_data, true, false);
1909 });
1910 }
1911
1912 if dependencies.read().unwrap().is_vanilla_data_loaded(false) {
1914 let game_path = settings.path_buf(game.key());
1915 let secondary_path = settings.path_buf(SECONDARY_PATH);
1916 let dependencies_file_path = dependencies_cache_path().unwrap().join(game.dependencies_cache_file_name());
1917 let pack_dependencies: Vec<_> = packs.values()
1918 .flat_map(|pack| pack.dependencies().iter().map(|x| x.1.clone()))
1919 .collect();
1920
1921 match dependencies.write().unwrap().rebuild(&schema, &pack_dependencies, Some(&*dependencies_file_path), game, &game_path, &secondary_path) {
1922 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
1923 Err(_) => CentralCommand::send_back(&sender, Response::Error("Schema updated, but dependencies cache rebuilding failed. You may need to regenerate it.".to_string())),
1924 }
1925 } else {
1926 CentralCommand::send_back(&sender, Response::Success)
1927 }
1928 },
1929 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
1930 }
1931 }
1932
1933 Command::UpdateLuaAutogen => {
1935 let sender = sender.clone();
1936 tokio::spawn(async move {
1937 let result = tokio::task::spawn_blocking(|| {
1938 match lua_autogen_base_path() {
1939 Ok(local_path) => {
1940 let git_integration = GitIntegration::new(&local_path, LUA_REPO, LUA_BRANCH, LUA_REMOTE);
1941 git_integration.update_repo().map(|_| ()).map_err(|e| e.into())
1942 },
1943 Err(error) => Err(error),
1944 }
1945 }).await.unwrap();
1946
1947 match result {
1948 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
1949 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
1950 }
1951 });
1952 }
1953
1954 Command::UpdateMainProgram => {
1956 let sender = sender.clone();
1957 let settings = settings.clone();
1958 tokio::spawn(async move {
1959 let result = tokio::task::spawn_blocking(move || {
1960 crate::updater::update_main_program(&settings)
1961 }).await.unwrap();
1962
1963 match result {
1964 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
1965 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
1966 }
1967 });
1968 }
1969
1970 Command::TriggerBackupAutosave(pack_key) => {
1972 match packs.get(&pack_key) {
1973 Some(pack) => {
1974 let folder = backup_autosave_path().unwrap().join(pack.disk_file_name());
1975 let _ = DirBuilder::new().recursive(true).create(&folder);
1976
1977 let game_path = settings.path_buf(game.key());
1978 let ca_paths = game.ca_packs_paths(&game_path)
1979 .unwrap_or_default()
1980 .iter()
1981 .map(|path| path.to_string_lossy().replace('\\', "/"))
1982 .collect::<Vec<_>>();
1983
1984 let pack_disable_autosaves = pack.settings().setting_bool("disable_autosaves")
1985 .unwrap_or(&true);
1986
1987 let pack_type = pack.pfh_file_type();
1988 let pack_path = pack.disk_file_path().replace('\\', "/");
1989
1990 if folder.is_dir() &&
1992 !pack_disable_autosaves &&
1993 (pack_type == PFHFileType::Mod || pack_type == PFHFileType::Movie) &&
1994 (ca_paths.is_empty() || !ca_paths.contains(&pack_path))
1995 {
1996 let date = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH).unwrap().as_secs();
1997 let new_name = format!("{date}.pack");
1998 let new_path = folder.join(new_name);
1999 let extra_data = Some(EncodeableExtraData::new_from_game_info_and_settings(game, pack.compression_format(), settings.bool("disable_uuid_regeneration_on_db_tables")));
2000 let _ = pack.clone().save(Some(&new_path), game, &extra_data);
2001
2002 if let Ok(files) = files_in_folder_from_newest_to_oldest(&folder) {
2004 let max_files = settings.i32("autosave_amount") as usize;
2005 for (index, file) in files.iter().enumerate() {
2006 if index >= max_files {
2007 let _ = std::fs::remove_file(file);
2008 }
2009 }
2010 }
2011 }
2012 CentralCommand::send_back(&sender, Response::Success);
2013 }
2014 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
2015 }
2016 }
2017
2018 Command::DiagnosticsCheck(diagnostics_ignored, check_ak_only_refs) => {
2020 let game_path = settings.path_buf(game.key());
2021 let mut diagnostics = Diagnostics::default();
2022 *diagnostics.diagnostics_ignored_mut() = diagnostics_ignored;
2023
2024 if let Some(ref schema) = schema {
2025 diagnostics.check(&mut packs, &mut dependencies.write().unwrap(), schema, game, &game_path, &[], check_ak_only_refs);
2026 }
2027
2028 info!("Checking diagnostics: done.");
2029
2030 CentralCommand::send_back(&sender, Response::Diagnostics(diagnostics));
2031 }
2032
2033 Command::DiagnosticsUpdate(mut diagnostics, path_types, check_ak_only_refs) => {
2034 let game_path = settings.path_buf(game.key());
2035
2036 if let Some(ref schema) = schema {
2037 diagnostics.check(&mut packs, &mut dependencies.write().unwrap(), schema, game, &game_path, &path_types, check_ak_only_refs);
2038 }
2039
2040 info!("Checking diagnostics (update): done.");
2041
2042 CentralCommand::send_back(&sender, Response::Diagnostics(diagnostics));
2043 }
2044
2045 Command::GetPackSettings(pack_key) => {
2047 match packs.get(&pack_key) {
2048 Some(pack) => CentralCommand::send_back(&sender, Response::PackSettings(pack.settings().clone())),
2049 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
2050 }
2051 }
2052 Command::SetPackSettings(pack_key, pack_settings) => {
2053 match packs.get_mut(&pack_key) {
2054 Some(pack) => {
2055 pack.set_settings(pack_settings);
2056 CentralCommand::send_back(&sender, Response::Success);
2057 }
2058 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
2059 }
2060 }
2061
2062 Command::GetMissingDefinitions(pack_key) => {
2063 match packs.get_mut(&pack_key) {
2064 Some(pack) => {
2065 let mut counter = 0;
2068 let mut table_list = String::new();
2069 if let Some(ref schema) = schema {
2070 let mut extra_data = DecodeableExtraData::default();
2071 extra_data.set_schema(Some(schema));
2072 let extra_data = Some(extra_data);
2073
2074 let mut files = pack.files_by_type_mut(&[FileType::DB]);
2075 files.sort_by_key(|file| file.path_in_container_raw().to_lowercase());
2076
2077 for file in files {
2078 if file.decode(&extra_data, false, false).is_err() && file.load().is_ok() {
2079 if let Ok(raw_data) = file.cached() {
2080 let mut reader = Cursor::new(raw_data);
2081 if let Ok((_, _, _, entry_count)) = DB::read_header(&mut reader) {
2082 if entry_count > 0 {
2083 counter += 1;
2084 table_list.push_str(&format!("{}, {:?}\n", counter, file.path_in_container_raw()))
2085 }
2086 }
2087 }
2088 }
2089 }
2090 }
2091
2092 let path = exe_path().join("missing_table_definitions.txt");
2094
2095 if let Ok(file) = File::create(path) {
2096 let mut file = BufWriter::new(file);
2097 let _ = file.write_all(table_list.as_bytes());
2098 }
2099 CentralCommand::send_back(&sender, Response::Success);
2100 }
2101 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
2102 }
2103 }
2104
2105 Command::RebuildDependencies(rebuild_only_current_mod_dependencies) => {
2107 if schema.is_some() {
2108 let game_path = settings.path_buf(game.key());
2109 let dependencies_file_path = dependencies_cache_path().unwrap().join(game.dependencies_cache_file_name());
2110 let file_path = if !rebuild_only_current_mod_dependencies { Some(&*dependencies_file_path) } else { None };
2111 let pack_dependencies: Vec<_> = packs.values()
2112 .flat_map(|pack| pack.dependencies().iter().map(|x| x.1.clone()))
2113 .collect();
2114
2115 let secondary_path = settings.path_buf(SECONDARY_PATH);
2116 let _ = dependencies.write().unwrap().rebuild(&schema, &pack_dependencies, file_path, game, &game_path, &secondary_path);
2117 let dependencies_info = DependenciesInfo::new(&dependencies.read().unwrap(), game.vanilla_db_table_name_logic());
2118 CentralCommand::send_back(&sender, Response::DependenciesInfo(dependencies_info));
2119 } else {
2120 CentralCommand::send_back(&sender, Response::Error(anyhow!("There is no Schema for the Game Selected.").to_string()));
2121 }
2122 },
2123
2124 Command::CascadeEdition(pack_key, table_name, definition, changes) => {
2125 match packs.get_mut(&pack_key) {
2126 Some(pack) => {
2127 let edited_paths = if let Some(ref schema) = schema {
2128 changes.iter().flat_map(|(field, value_before, value_after)| {
2129 DB::cascade_edition(pack, schema, &table_name, field, &definition, value_before, value_after)
2130 }).collect::<Vec<_>>()
2131 } else { vec![] };
2132
2133 let packed_files_info = pack.files_by_paths(&edited_paths, false).into_par_iter().map(From::from).collect();
2134 CentralCommand::send_back(&sender, Response::VecContainerPathVecRFileInfo(edited_paths, packed_files_info));
2135 }
2136 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
2137 }
2138 },
2139
2140 Command::GetTablesByTableName(pack_key, table_name) => {
2141 match packs.get(&pack_key) {
2142 Some(pack) => {
2143 let path = ContainerPath::Folder(format!("db/{table_name}/"));
2144 let files = pack.files_by_type_and_paths(&[FileType::DB], &[path], true);
2145 let paths = files.iter()
2146 .map(|x| x.path_in_container_raw().to_owned())
2147 .collect::<Vec<_>>();
2148
2149 CentralCommand::send_back(&sender, Response::VecString(paths));
2150 }
2151 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
2152 }
2153 },
2154
2155 Command::AddKeysToKeyDeletes(pack_key, table_file_name, key_table_name, keys) => {
2156 match packs.get_mut(&pack_key) {
2157 Some(pack) => {
2158 let path = ContainerPath::File(format!("db/{KEY_DELETES_TABLE_NAME}/{table_file_name}"));
2159 let mut files = pack.files_by_type_and_paths_mut(&[FileType::DB], &[path], true);
2160
2161 let mut cont_path = None;
2162 if let Some(file) = files.first_mut() {
2163 if let Ok(RFileDecoded::DB(db)) = file.decoded_mut() {
2164 for key in &keys {
2165 let row = vec![
2166 DecodedData::StringU8(key.to_owned()),
2167 DecodedData::StringU8(key_table_name.to_owned()),
2168 ];
2169
2170 db.data_mut().push(row);
2171 }
2172
2173 cont_path = Some(file.path_in_container());
2174 }
2175 }
2176
2177 CentralCommand::send_back(&sender, Response::OptionContainerPath(cont_path));
2178 }
2179 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
2180 }
2181 }
2182
2183 Command::GoToDefinition(pack_key, ref_table, mut ref_column, ref_data) => {
2184 let table_name = format!("{ref_table}_tables");
2185 let table_folder = format!("db/{table_name}");
2186 let Some(pack) = get_pack(&packs, &pack_key, &sender) else { continue 'background_loop; };
2187 let packed_files = pack.files_by_path(&ContainerPath::Folder(table_folder.to_owned()), true);
2188 let mut found = false;
2189 for packed_file in &packed_files {
2190 if let Ok(RFileDecoded::DB(data)) = packed_file.decoded() {
2191
2192 if data.definition().localised_fields().iter().any(|x| x.name() == ref_column) {
2194 if let Some(first_key_index) = data.definition().localised_key_order().first() {
2195 if let Some(first_key_field) = data.definition().fields_processed().get(*first_key_index as usize) {
2196 ref_column = first_key_field.name().to_owned();
2197 }
2198 }
2199 }
2200
2201 if let Some((column_index, row_index)) = data.table().rows_containing_data(&ref_column, &ref_data[0]) {
2202 CentralCommand::send_back(&sender, Response::DataSourceStringUsizeUsize(DataSource::PackFile, packed_file.path_in_container_raw().to_owned(), column_index, row_index[0]));
2203 found = true;
2204 break;
2205 }
2206 }
2207 }
2208
2209 if !found {
2210 if let Ok(packed_files) = dependencies.read().unwrap().db_data(&table_name, false, true) {
2211 for packed_file in &packed_files {
2212 if let Ok(RFileDecoded::DB(data)) = packed_file.decoded() {
2213
2214 if data.definition().localised_fields().iter().any(|x| x.name() == ref_column) {
2216 if let Some(first_key_index) = data.definition().localised_key_order().first() {
2217 if let Some(first_key_field) = data.definition().fields_processed().get(*first_key_index as usize) {
2218 ref_column = first_key_field.name().to_owned();
2219 }
2220 }
2221 }
2222
2223 if let Some((column_index, row_index)) = data.table().rows_containing_data(&ref_column, &ref_data[0]) {
2224 CentralCommand::send_back(&sender, Response::DataSourceStringUsizeUsize(DataSource::ParentFiles, packed_file.path_in_container_raw().to_owned(), column_index, row_index[0]));
2225 found = true;
2226 break;
2227 }
2228 }
2229 }
2230 }
2231 }
2232
2233 if !found {
2234 if let Ok(packed_files) = dependencies.read().unwrap().db_data(&table_name, true, false) {
2235 for packed_file in &packed_files {
2236 if let Ok(RFileDecoded::DB(data)) = packed_file.decoded() {
2237
2238 if data.definition().localised_fields().iter().any(|x| x.name() == ref_column) {
2240 if let Some(first_key_index) = data.definition().localised_key_order().first() {
2241 if let Some(first_key_field) = data.definition().fields_processed().get(*first_key_index as usize) {
2242 ref_column = first_key_field.name().to_owned();
2243 }
2244 }
2245 }
2246
2247 if let Some((column_index, row_index)) = data.table().rows_containing_data(&ref_column, &ref_data[0]) {
2248 CentralCommand::send_back(&sender, Response::DataSourceStringUsizeUsize(DataSource::GameFiles, packed_file.path_in_container_raw().to_owned(), column_index, row_index[0]));
2249 found = true;
2250 break;
2251 }
2252 }
2253 }
2254 }
2255 }
2256
2257 if !found {
2258 if let Some(data) = dependencies.read().unwrap().asskit_only_db_tables().get(&table_name) {
2259
2260 if data.definition().localised_fields().iter().any(|x| x.name() == ref_column) {
2262 if let Some(first_key_index) = data.definition().localised_key_order().first() {
2263 if let Some(first_key_field) = data.definition().fields_processed().get(*first_key_index as usize) {
2264 ref_column = first_key_field.name().to_owned();
2265 }
2266 }
2267 }
2268
2269 if let Some((column_index, row_index)) = data.table().rows_containing_data(&ref_column, &ref_data[0]) {
2270 let path = format!("{}/ak_data", &table_folder);
2271 CentralCommand::send_back(&sender, Response::DataSourceStringUsizeUsize(DataSource::AssKitFiles, path, column_index, row_index[0]));
2272 found = true;
2273 }
2274 }
2275 }
2276
2277 if !found {
2278 CentralCommand::send_back(&sender, Response::Error(tr("source_data_for_field_not_found")));
2279 }
2280 },
2281
2282 Command::SearchReferences(pack_key, reference_map, value) => {
2283 let paths = reference_map.keys().map(|x| ContainerPath::Folder(format!("db/{x}"))).collect::<Vec<ContainerPath>>();
2284 let Some(pack) = get_pack(&packs, &pack_key, &sender) else { continue 'background_loop; };
2285 let files = pack.files_by_paths(&paths, true);
2286
2287 let mut references: Vec<(DataSource, String, String, String, usize, usize)> = vec![];
2288
2289 for (table_name, columns) in &reference_map {
2292 for file in &files {
2293 if file.db_table_name_from_path().unwrap() == table_name {
2294 if let Ok(RFileDecoded::DB(data)) = file.decoded() {
2295 for column_name in columns {
2296 if let Some((column_index, row_indexes)) = data.table().rows_containing_data(column_name, &value) {
2297 for row_index in &row_indexes {
2298 references.push((DataSource::PackFile, pack_key.clone(), file.path_in_container_raw().to_owned(), column_name.to_owned(), column_index, *row_index));
2299 }
2300 }
2301 }
2302 }
2303 }
2304 }
2305 }
2306
2307 for (table_name, columns) in &reference_map {
2311 if let Ok(tables) = dependencies.read().unwrap().db_data(table_name, false, true) {
2312 references.append(&mut tables.par_iter().map(|table| {
2313 let mut references = vec![];
2314 if let Ok(RFileDecoded::DB(data)) = table.decoded() {
2315 for column_name in columns {
2316 if let Some((column_index, row_indexes)) = data.table().rows_containing_data(column_name, &value) {
2317 for row_index in &row_indexes {
2318 references.push((DataSource::ParentFiles, String::new(), table.path_in_container_raw().to_owned(), column_name.to_owned(), column_index, *row_index));
2319 }
2320 }
2321 }
2322 }
2323
2324 references
2325 }).flatten().collect());
2326 }
2327 }
2328
2329 for (table_name, columns) in &reference_map {
2331 if let Ok(tables) = dependencies.read().unwrap().db_data(table_name, true, false) {
2332 references.append(&mut tables.par_iter().map(|table| {
2333 let mut references = vec![];
2334 if let Ok(RFileDecoded::DB(data)) = table.decoded() {
2335 for column_name in columns {
2336 if let Some((column_index, row_indexes)) = data.table().rows_containing_data(column_name, &value) {
2337 for row_index in &row_indexes {
2338 references.push((DataSource::GameFiles, String::new(), table.path_in_container_raw().to_owned(), column_name.to_owned(), column_index, *row_index));
2339 }
2340 }
2341 }
2342 }
2343
2344 references
2345 }).flatten().collect());
2346 }
2347 }
2348
2349 CentralCommand::send_back(&sender, Response::VecDataSourceStringStringStringUsizeUsize(references));
2350 },
2351
2352 Command::GoToLoc(pack_key, loc_key) => {
2353 let Some(pack) = get_pack(&packs, &pack_key, &sender) else { continue 'background_loop; };
2354 let packed_files = pack.files_by_type(&[FileType::Loc]);
2355 let mut found = false;
2356 for packed_file in &packed_files {
2357 if let Ok(RFileDecoded::Loc(data)) = packed_file.decoded() {
2358 if let Some((column_index, row_index)) = data.table().rows_containing_data("key", &loc_key) {
2359 CentralCommand::send_back(&sender, Response::DataSourceStringUsizeUsize(DataSource::PackFile, packed_file.path_in_container_raw().to_owned(), column_index, row_index[0]));
2360 found = true;
2361 break;
2362 }
2363 }
2364 }
2365
2366 if !found {
2367 if let Ok(packed_files) = dependencies.read().unwrap().loc_data(false, true) {
2368 for packed_file in &packed_files {
2369 if let Ok(RFileDecoded::Loc(data)) = packed_file.decoded() {
2370 if let Some((column_index, row_index)) = data.table().rows_containing_data("key", &loc_key) {
2371 CentralCommand::send_back(&sender, Response::DataSourceStringUsizeUsize(DataSource::ParentFiles, packed_file.path_in_container_raw().to_owned(), column_index, row_index[0]));
2372 found = true;
2373 break;
2374 }
2375 }
2376 }
2377 }
2378 }
2379
2380 if !found {
2381 if let Ok(packed_files) = dependencies.read().unwrap().loc_data(true, false) {
2382 for packed_file in &packed_files {
2383 if let Ok(RFileDecoded::Loc(data)) = packed_file.decoded() {
2384 if let Some((column_index, row_index)) = data.table().rows_containing_data("key", &loc_key) {
2385 CentralCommand::send_back(&sender, Response::DataSourceStringUsizeUsize(DataSource::GameFiles, packed_file.path_in_container_raw().to_owned(), column_index, row_index[0]));
2386 found = true;
2387 break;
2388 }
2389 }
2390 }
2391 }
2392 }
2393
2394 if !found {
2395 CentralCommand::send_back(&sender, Response::Error(tr("loc_key_not_found")));
2396 }
2397 },
2398
2399 Command::GetSourceDataFromLocKey(_pack_key, loc_key) => CentralCommand::send_back(&sender, Response::OptionStringStringVecString(dependencies.read().unwrap().loc_key_source(&loc_key))),
2400 Command::GetPackFileName(pack_key) => {
2401 match packs.get(&pack_key) {
2402 Some(pack) => CentralCommand::send_back(&sender, Response::String(pack.disk_file_name())),
2403 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
2404 }
2405 }
2406 Command::GetPackedFileRawData(pack_key, path) => {
2407 match packs.get_mut(&pack_key) {
2408 Some(pack) => {
2409 let cf = pack.compression_format();
2410 match pack.files_mut().get_mut(&path) {
2411 Some(ref mut rfile) => {
2412
2413 match rfile.load() {
2415 Ok(_) => match rfile.cached() {
2416 Ok(data) => CentralCommand::send_back(&sender, Response::VecU8(data.to_vec())),
2417
2418 Err(_) => {
2422 let extra_data = Some(EncodeableExtraData::new_from_game_info_and_settings(game, cf, settings.bool("disable_uuid_regeneration_on_db_tables")));
2423 match rfile.encode(&extra_data, false, false, true) {
2424 Ok(data) => CentralCommand::send_back(&sender, Response::VecU8(data.unwrap())),
2425 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
2426 }
2427 },
2428 },
2429 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
2430 }
2431 }
2432 None => CentralCommand::send_back(&sender, Response::Error(anyhow!("This PackedFile no longer exists in the PackFile.").to_string())),
2433 }
2434 }
2435 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
2436 }
2437 },
2438
2439 Command::ImportDependenciesToOpenPackFile(pack_key, paths_by_data_source) => {
2440 match packs.get_mut(&pack_key) {
2441 Some(pack) => {
2442 let mut added_paths = vec![];
2443 let mut not_added_paths = vec![];
2444
2445 let dependencies = dependencies.read().unwrap();
2446 for (data_source, paths) in &paths_by_data_source {
2447 let files = match data_source {
2448 DataSource::GameFiles => dependencies.files_by_path(paths, true, false, false),
2449 DataSource::ParentFiles => dependencies.files_by_path(paths, false, true, false),
2450 DataSource::AssKitFiles => HashMap::new(),
2451 _ => {
2452 CentralCommand::send_back(&sender, Response::Error("You can't import files from this source.".to_string()));
2453 continue 'background_loop;
2454 },
2455 };
2456
2457 for file in files.into_values() {
2458 let file_path = file.path_in_container_raw().to_owned();
2459 let mut file = file.clone();
2460 let _ = file.guess_file_type();
2461 if let Ok(Some(path)) = pack.insert(file) {
2462 added_paths.push(path);
2463 } else {
2464 not_added_paths.push(file_path);
2465 }
2466 }
2467 }
2468
2469 for (data_source, paths) in &paths_by_data_source {
2471 match data_source {
2472 DataSource::GameFiles | DataSource::ParentFiles => {},
2473 DataSource::AssKitFiles => {
2474 match &schema {
2475 Some(ref schema) => {
2476 let mut files = vec![];
2477 for path in paths {
2478
2479 match path {
2481 ContainerPath::Folder(path) => {
2482 let mut path = path.to_owned();
2483
2484 if path.ends_with('/') {
2485 path.pop();
2486 }
2487
2488 let path_split = path.split('/').collect::<Vec<_>>();
2489 let table_name_logic = game.vanilla_db_table_name_logic();
2490
2491 if path_split.len() == 1 {
2493 let table_names = dependencies.asskit_only_db_tables().keys();
2494 for table_name in table_names {
2495 let table_file_name = match table_name_logic {
2496 VanillaDBTableNameLogic::DefaultName(ref name) => name,
2497 VanillaDBTableNameLogic::FolderName => table_name,
2498 };
2499
2500 match dependencies.import_from_ak(table_name, schema) {
2501 Ok(table) => {
2502 let mut path = path_split.to_vec();
2503 path.push(table_file_name);
2504 let mut path = path.join("/");
2505
2506 if table_name.starts_with("ceo") {
2507 path = format!("ceo_{path}");
2508 }
2509
2510 let file = RFile::new_from_decoded(&RFileDecoded::DB(table), 0, &path);
2511 files.push(file);
2512 },
2513 Err(_) => not_added_paths.push(path.clone()),
2514 }
2515 }
2516 }
2517
2518 else if path_split.len() == 2 {
2520
2521 let table_name = path_split[1];
2522 let table_file_name = match table_name_logic {
2523 VanillaDBTableNameLogic::DefaultName(ref name) => name,
2524 VanillaDBTableNameLogic::FolderName => table_name,
2525 };
2526
2527 match dependencies.import_from_ak(table_name, schema) {
2528 Ok(table) => {
2529 let mut path = path_split.to_vec();
2530 path.push(table_file_name);
2531 let mut path = path.join("/");
2532
2533 if table_name.starts_with("ceo") {
2534 path = format!("ceo_{path}");
2535 }
2536
2537 let file = RFile::new_from_decoded(&RFileDecoded::DB(table), 0, &path);
2538 files.push(file);
2539 },
2540 Err(_) => not_added_paths.push(path.clone()),
2541 }
2542 }
2543
2544 else {
2546 CentralCommand::send_back(&sender, Response::Error("No idea how you were able to trigger this.".to_string()));
2547 continue 'background_loop;
2548 }
2549
2550 }
2551 ContainerPath::File(path) => {
2552 let table_name = path.split('/').collect::<Vec<_>>()[1];
2553 match dependencies.import_from_ak(table_name, schema) {
2554 Ok(table) => {
2555 let file_path = if table_name.starts_with("ceo") {
2556 format!("ceo_{}", path)
2557 } else {
2558 path.clone()
2559 };
2560
2561 let file = RFile::new_from_decoded(&RFileDecoded::DB(table), 0, &file_path);
2562 files.push(file);
2563 },
2564 Err(_) => not_added_paths.push(path.clone()),
2565 }
2566 }
2567 }
2568 }
2569
2570 for file in files {
2571 if let Ok(Some(path)) = pack.insert(file) {
2572 added_paths.push(path);
2573 }
2574 }
2575 },
2576 None => {
2577 CentralCommand::send_back(&sender, Response::Error(anyhow!("There is no Schema for the Game Selected.").to_string()));
2578 continue 'background_loop;
2579 }
2580 }
2581 },
2582 _ => {
2583 CentralCommand::send_back(&sender, Response::Error("You can't import files from this source.".to_string()));
2584 continue 'background_loop;
2585 },
2586 }
2587 }
2588
2589 CentralCommand::send_back(&sender, Response::VecContainerPathVecString(added_paths, not_added_paths));
2590 }
2591 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
2592 }
2593 },
2594
2595 Command::GetRFilesFromAllSources(paths, force_lowercased_paths) => {
2596 let mut packed_files = HashMap::new();
2597 let dependencies = dependencies.read().unwrap();
2598
2599 let mut packed_files_parent = HashMap::new();
2601 for (path, file) in dependencies.files_by_path(&paths, false, true, true) {
2602 packed_files_parent.insert(if force_lowercased_paths { path.to_lowercase() } else { path }, file.clone());
2603 }
2604
2605 let mut packed_files_game = HashMap::new();
2607 for (path, file) in dependencies.files_by_path(&paths, true, false, true) {
2608 packed_files_game.insert(if force_lowercased_paths { path.to_lowercase() } else { path }, file.clone());
2609 }
2610
2611 let mut packed_files_packfile = HashMap::new();
2622 for pack in packs.values() {
2623 for file in pack.files_by_paths(&paths, true) {
2624 packed_files_packfile.insert(if force_lowercased_paths { file.path_in_container_raw().to_lowercase() } else { file.path_in_container_raw().to_owned() }, file.clone());
2625 }
2626 }
2627
2628 packed_files.insert(DataSource::ParentFiles, packed_files_parent);
2629 packed_files.insert(DataSource::GameFiles, packed_files_game);
2630 packed_files.insert(DataSource::PackFile, packed_files_packfile);
2631
2632 CentralCommand::send_back(&sender, Response::HashMapDataSourceHashMapStringRFile(packed_files));
2634 },
2635
2636 Command::GetAnimPathsBySkeletonName(skeleton_name) => {
2637 let mut paths = HashSet::new();
2638 let mut dependencies = dependencies.write().unwrap();
2639
2640 let mut packed_files_parent = HashSet::new();
2642 for (path, file) in dependencies.files_by_types_mut(&[FileType::Anim], false, true) {
2643 if let Ok(Some(RFileDecoded::Anim(file))) = file.decode(&None, false, true) {
2644 if file.skeleton_name() == &skeleton_name {
2645 packed_files_parent.insert(path);
2646 }
2647 }
2648 }
2649
2650 let mut packed_files_game = HashSet::new();
2652 for (path, file) in dependencies.files_by_types_mut(&[FileType::Anim], true, false) {
2653 if let Ok(Some(RFileDecoded::Anim(file))) = file.decode(&None, false, true) {
2654 if file.skeleton_name() == &skeleton_name {
2655 packed_files_game.insert(path);
2656 }
2657 }
2658 }
2659
2660 let mut packed_files_packfile = HashSet::new();
2662 for pack in packs.values_mut() {
2663 for file in pack.files_by_type_mut(&[FileType::Anim]) {
2664 if let Ok(Some(RFileDecoded::Anim(anim_file))) = file.decode(&None, false, true) {
2665 if anim_file.skeleton_name() == &skeleton_name {
2666 packed_files_packfile.insert(file.path_in_container_raw().to_owned());
2667 }
2668 }
2669 }
2670 }
2671
2672 paths.extend(packed_files_game);
2673 paths.extend(packed_files_parent);
2674 paths.extend(packed_files_packfile);
2675
2676 CentralCommand::send_back(&sender, Response::HashSetString(paths));
2678 },
2679
2680 Command::GetPackedFilesNamesStartingWitPathFromAllSources(path) => {
2681 let mut files: HashMap<DataSource, HashSet<ContainerPath>> = HashMap::new();
2682 let dependencies = dependencies.read().unwrap();
2683
2684 let parent_files = dependencies.files_by_path(std::slice::from_ref(&path), false, true, true);
2685 if !parent_files.is_empty() {
2686 files.insert(DataSource::ParentFiles, parent_files.into_keys().map(ContainerPath::File).collect());
2687 }
2688
2689 let game_files = dependencies.files_by_path(std::slice::from_ref(&path), true, false, true);
2690 if !game_files.is_empty() {
2691 files.insert(DataSource::GameFiles, game_files.into_keys().map(ContainerPath::File).collect());
2692 }
2693
2694 let mut local_file_paths = HashSet::new();
2695 for pack in packs.values() {
2696 for file in pack.files_by_path(&path, true) {
2697 local_file_paths.insert(file.path_in_container());
2698 }
2699 }
2700 if !local_file_paths.is_empty() {
2701 files.insert(DataSource::PackFile, local_file_paths);
2702 }
2703
2704 CentralCommand::send_back(&sender, Response::HashMapDataSourceHashSetContainerPath(files));
2706 },
2707
2708 Command::SavePackedFilesToPackFileAndClean(pack_key, files, optimize) => {
2709 match packs.get_mut(&pack_key) {
2710 Some(pack) => {
2711 match &schema {
2712 Some(ref schema) => {
2713
2714 let mut added_paths = vec![];
2717 for file in files {
2718 if let Ok(Some(path)) = pack.insert(file) {
2719 added_paths.push(path);
2720 }
2721 }
2722
2723 added_paths.sort();
2725 added_paths.dedup();
2726
2727 if optimize {
2728
2729 let options = settings.optimizer_options();
2731
2732 match pack.optimize(None, &mut dependencies.write().unwrap(), schema, game, &options) {
2734 Ok((paths_to_delete, paths_to_add)) => {
2735 added_paths.extend(paths_to_add.into_iter()
2736 .map(ContainerPath::File)
2737 .collect::<Vec<_>>());
2738 CentralCommand::send_back(&sender, Response::VecContainerPathVecContainerPath(added_paths, paths_to_delete.into_iter()
2739 .map(ContainerPath::File)
2740 .collect()));
2741 },
2742 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
2743 }
2744 } else {
2745 CentralCommand::send_back(&sender, Response::VecContainerPathVecContainerPath(added_paths, vec![]));
2746 }
2747 },
2748 None => CentralCommand::send_back(&sender, Response::Error(anyhow!("There is no Schema for the Game Selected.").to_string())),
2749 }
2750 }
2751 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
2752 }
2753 },
2754
2755 Command::NotesForPath(pack_key, path) => {
2756 match packs.get(&pack_key) {
2757 Some(pack) => CentralCommand::send_back(&sender, Response::VecNote(pack.notes().notes_by_path(&path))),
2758 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
2759 }
2760 }
2761 Command::AddNote(pack_key, note) => {
2762 match packs.get_mut(&pack_key) {
2763 Some(pack) => CentralCommand::send_back(&sender, Response::Note(pack.notes_mut().add_note(note))),
2764 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
2765 }
2766 }
2767 Command::DeleteNote(pack_key, path, id) => {
2768 match packs.get_mut(&pack_key) {
2769 Some(pack) => {
2770 pack.notes_mut().delete_note(&path, id);
2771 CentralCommand::send_back(&sender, Response::Success);
2772 }
2773 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
2774 }
2775 }
2776
2777 Command::SaveLocalSchemaPatch(patches) => {
2778 let path = table_patches_path().unwrap().join(game.schema_file_name());
2779 match Schema::save_patches(&patches, &path) {
2780 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
2781 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
2782 }
2783 }
2784 Command::RemoveLocalSchemaPatchesForTable(table_name) => {
2785 let path = table_patches_path().unwrap().join(game.schema_file_name());
2786 match Schema::remove_patches_for_table(&table_name, &path) {
2787 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
2788 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
2789 }
2790 }
2791 Command::RemoveLocalSchemaPatchesForTableAndField(table_name, field_name) => {
2792 let path = table_patches_path().unwrap().join(game.schema_file_name());
2793 match Schema::remove_patches_for_table_and_field(&table_name, &field_name, &path) {
2794 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
2795 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
2796 }
2797 }
2798 Command::ImportSchemaPatch(patch) => {
2799 match schema {
2800 Some(ref mut schema) => {
2801 Schema::add_patches_to_patch_set(schema.patches_mut(), &patch);
2802 match schema.save(&schemas_path().unwrap().join(game.schema_file_name())) {
2803 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
2804 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
2805 }
2806 }
2807 None => CentralCommand::send_back(&sender, Response::Error(anyhow!("There is no Schema for the Game Selected.").to_string())),
2808 }
2809 }
2810
2811 Command::GenerateMissingLocData(_pack_key) => {
2812 match dependencies.read().unwrap().generate_missing_loc_data(&mut packs) {
2813 Ok(path) => CentralCommand::send_back(&sender, Response::VecContainerPath(path)),
2814 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
2815 }
2816 }
2817
2818 Command::PackMap(pack_key, tile_maps, tiles) => {
2819 match schema {
2820 Some(ref schema) => {
2821 let mut dependencies = dependencies.write().unwrap();
2822 let options = settings.optimizer_options();
2823 match dependencies.add_tile_maps_and_tiles(&mut packs, Some(&pack_key), game, schema, options, tile_maps, tiles) {
2824 Ok((paths_to_add, paths_to_delete)) => CentralCommand::send_back(&sender, Response::VecContainerPathVecContainerPath(paths_to_add, paths_to_delete)),
2825 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
2826 }
2827 }
2828 None => CentralCommand::send_back(&sender, Response::Error(anyhow!("There is no Schema for the Game Selected.").to_string())),
2829 }
2830 }
2831
2832 Command::InitializeMyModFolder(mod_name, mod_game, sublime_support, vscode_support, git_support) => {
2834 let mut mymod_path = settings.path_buf(MYMOD_BASE_PATH);
2835 if !mymod_path.is_dir() {
2836 CentralCommand::send_back(&sender, Response::Error("MyMod path is not configured. Configure it in the settings and try again.".to_string()));
2837 continue;
2838 }
2839
2840 mymod_path.push(&mod_game);
2841
2842 if let Err(error) = DirBuilder::new().recursive(true).create(&mymod_path) {
2844 CentralCommand::send_back(&sender, Response::Error(format!("Error while creating the MyMod's Game folder: {}.", error)));
2845 continue;
2846 }
2847
2848 mymod_path.push(&mod_name);
2850 if let Err(error) = DirBuilder::new().recursive(true).create(&mymod_path) {
2851 CentralCommand::send_back(&sender, Response::Error(format!("Error while creating the MyMod's Assets folder: {}.", error)));
2852 continue;
2853 };
2854
2855 if let Some(gitignore) = git_support {
2857 let git_integration = GitIntegration::new(&mymod_path, "", "", "");
2858 if let Err(error) = git_integration.init() {
2859 CentralCommand::send_back(&sender, Response::Error(error.to_string()));
2860 continue
2861 }
2862
2863 if let Err(error) = git_integration.add_gitignore(&gitignore) {
2864 CentralCommand::send_back(&sender, Response::Error(error.to_string()));
2865 continue
2866 }
2867 }
2868
2869 if sublime_support || vscode_support {
2871 if let Ok(lua_autogen_folder) = lua_autogen_game_path(game) {
2872 let lua_autogen_folder = lua_autogen_folder.to_string_lossy().to_string().replace('\\', "/");
2873
2874 if vscode_support {
2876 let mut vscode_config_path = mymod_path.to_owned();
2877 vscode_config_path.push(".vscode");
2878
2879 if let Err(error) = DirBuilder::new().recursive(true).create(&vscode_config_path) {
2880 CentralCommand::send_back(&sender, Response::Error(format!("Error while creating the VSCode Config folder: {}.", error)));
2881 continue;
2882 };
2883
2884 let mut vscode_extensions_path_file = vscode_config_path.to_owned();
2885 vscode_extensions_path_file.push("extensions.json");
2886 if let Ok(file) = File::create(vscode_extensions_path_file) {
2887 let mut file = BufWriter::new(file);
2888 let _ = file.write_all("
2889{
2890 \"recommendations\": [
2891 \"sumneko.lua\",
2892 \"formulahendry.code-runner\"
2893 ],
2894}".as_bytes());
2895 }
2896 }
2897
2898 if sublime_support {
2900 let mut sublime_config_path = mymod_path.to_owned();
2901 sublime_config_path.push(format!("{mod_name}.sublime-project"));
2902 if let Ok(file) = File::create(sublime_config_path) {
2903 let mut file = BufWriter::new(file);
2904 let _ = file.write_all("
2905{
2906 \"folders\":
2907 [
2908 {
2909 \"path\": \".\"
2910 }
2911 ]
2912}".to_string().as_bytes());
2913 }
2914 }
2915
2916 let mut luarc_config_path = mymod_path.to_owned();
2918 luarc_config_path.push(".luarc.json");
2919
2920 if let Ok(file) = File::create(luarc_config_path) {
2921 let mut file = BufWriter::new(file);
2922 let _ = file.write_all(format!("
2923{{
2924 \"workspace.library\": [
2925 \"{lua_autogen_folder}/global/\",
2926 \"{lua_autogen_folder}/campaign/\",
2927 \"{lua_autogen_folder}/frontend/\",
2928 \"{lua_autogen_folder}/battle/\"
2929 ],
2930 \"runtime.version\": \"Lua 5.1\",
2931 \"completion.autoRequire\": false,
2932 \"workspace.preloadFileSize\": 1500,
2933 \"workspace.ignoreSubmodules\": false,
2934 \"diagnostics.workspaceDelay\": 500,
2935 \"diagnostics.workspaceRate\": 40,
2936 \"diagnostics.disable\": [
2937 \"lowercase-global\",
2938 \"trailing-space\"
2939 ],
2940 \"hint.setType\": true,
2941 \"workspace.ignoreDir\": [
2942 \".vscode\",
2943 \".git\"
2944 ]
2945}}").as_bytes());
2946 }
2947 }
2948 }
2949
2950 mymod_path.set_extension("pack");
2952 CentralCommand::send_back(&sender, Response::PathBuf(mymod_path));
2953 },
2954
2955 Command::LiveExport(pack_key) => {
2956 match packs.get_mut(&pack_key) {
2957 Some(pack) => {
2958 let game_path = settings.path_buf(game.key());
2959 let disable_regen_table_guid = settings.bool("disable_uuid_regeneration_on_db_tables");
2960 let keys_first = settings.bool("tables_use_old_column_order_for_tsv");
2961 match pack.live_export(game, &game_path, disable_regen_table_guid, keys_first) {
2962 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
2963 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
2964 }
2965 }
2966 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
2967 }
2968 },
2969
2970 Command::SetPackOperationalMode(pack_key, mode) => {
2971 if packs.contains_key(&pack_key) {
2972 pack_modes.insert(pack_key, mode);
2973 CentralCommand::send_back(&sender, Response::Success);
2974 } else {
2975 CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key)));
2976 }
2977 },
2978
2979 Command::GetPackOperationalMode(pack_key) => {
2980 let mode = pack_modes.get(&pack_key).cloned().unwrap_or(OperationalMode::Normal);
2981 CentralCommand::send_back(&sender, Response::OperationalMode(mode));
2982 },
2983
2984 Command::AddLineToPackIgnoredDiagnostics(pack_key, line) => {
2985 match packs.get_mut(&pack_key) {
2986 Some(pack) => {
2987 if let Some(diagnostics_ignored) = pack.settings_mut().settings_text_mut().get_mut("diagnostics_files_to_ignore") {
2988 diagnostics_ignored.push_str(&line);
2989 } else {
2990 pack.settings_mut().settings_text_mut().insert("diagnostics_files_to_ignore".to_owned(), line);
2991 }
2992 CentralCommand::send_back(&sender, Response::Success);
2993 }
2994 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
2995 }
2996 },
2997
2998 Command::UpdateEmpireAndNapoleonAK => {
2999 let sender = sender.clone();
3000 tokio::spawn(async move {
3001 let result = tokio::task::spawn_blocking(|| {
3002 match old_ak_files_path() {
3003 Ok(local_path) => {
3004 let git_integration = GitIntegration::new(&local_path, OLD_AK_REPO, OLD_AK_BRANCH, OLD_AK_REMOTE);
3005 git_integration.update_repo().map(|_| ()).map_err(|e| e.into())
3006 },
3007 Err(error) => Err(error),
3008 }
3009 }).await.unwrap();
3010
3011 match result {
3012 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
3013 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
3014 }
3015 });
3016 }
3017
3018 Command::GetPackTranslation(pack_key, language) => {
3019 let game_key = game.key();
3020 match translations_local_path() {
3021 Ok(local_path) => {
3022 let mut base_english = HashMap::new();
3023 let mut base_local_fixes = HashMap::new();
3024
3025 match translations_remote_path() {
3026 Ok(remote_path) => {
3027
3028 let vanilla_loc_path = remote_path.join(format!("{}/{}", game.key(), VANILLA_LOC_NAME));
3029 if let Ok(mut vanilla_loc) = RFile::tsv_import_from_path(&vanilla_loc_path, &None) {
3030 let _ = vanilla_loc.guess_file_type();
3031 if let Ok(RFileDecoded::Loc(vanilla_loc)) = vanilla_loc.decoded() {
3032
3033 let fixes_loc_path = remote_path.join(format!("{}/{}{}.tsv", game.key(), VANILLA_FIXES_NAME, language));
3035 if let Ok(mut fixes_loc) = RFile::tsv_import_from_path(&fixes_loc_path, &None) {
3036 let _ = fixes_loc.guess_file_type();
3037
3038 if let Ok(RFileDecoded::Loc(fixes_loc)) = fixes_loc.decoded() {
3039 base_local_fixes.extend(fixes_loc.data().iter().map(|x| (x[0].data_to_string().to_string(), x[1].data_to_string().to_string())).collect::<Vec<_>>());
3040 }
3041 }
3042
3043 base_english.extend(vanilla_loc.data().iter().map(|x| (x[0].data_to_string().to_string(), x[1].data_to_string().to_string())).collect::<Vec<_>>());
3044 }
3045 }
3046
3047 let dependencies = dependencies.read().unwrap();
3048 let paths = vec![local_path, remote_path];
3049 let Some(pack_ref) = get_pack(&packs, &pack_key, &sender) else { continue 'background_loop; };
3050 match PackTranslation::new(&paths, pack_ref, game_key, &language, &dependencies, &base_english, &base_local_fixes) {
3051 Ok(tr) => CentralCommand::send_back(&sender, Response::PackTranslation(tr)),
3052 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
3053 }
3054 }
3055 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
3056 }
3057 },
3058 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
3059 }
3060 }
3061
3062 Command::UpdateTranslations => {
3063 let sender = sender.clone();
3064 tokio::spawn(async move {
3065 let result = tokio::task::spawn_blocking(|| {
3066 match translations_remote_path() {
3067 Ok(local_path) => {
3068 let git_integration = GitIntegration::new(&local_path, TRANSLATIONS_REPO, TRANSLATIONS_BRANCH, TRANSLATIONS_REMOTE);
3069 git_integration.update_repo().map(|_| ()).map_err(|e| e.into())
3070 },
3071 Err(error) => Err(error),
3072 }
3073 }).await.unwrap();
3074
3075 match result {
3076 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
3077 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
3078 }
3079 });
3080 }
3081
3082 Command::BuildStarposGetCampaingIds(_pack_key) => {
3083 let ids = dependencies.read().unwrap().db_values_from_table_name_and_column_name(Some(&packs), "campaigns_tables", "campaign_name", true, true);
3084 CentralCommand::send_back(&sender, Response::HashSetString(ids));
3085 }
3086
3087 Command::BuildStarposCheckVictoryConditions(pack_key) => {
3088 let Some(pack_ref) = get_pack(&packs, &pack_key, &sender) else { continue 'background_loop; };
3089 if !GAMES_NEEDING_VICTORY_OBJECTIVES.contains(&game.key()) || (
3090 GAMES_NEEDING_VICTORY_OBJECTIVES.contains(&game.key()) &&
3091 pack_ref.file(VICTORY_OBJECTIVES_FILE_NAME, false).is_some()
3092 ) {
3093 CentralCommand::send_back(&sender, Response::Success);
3094 } else {
3095 CentralCommand::send_back(&sender, Response::Error("Missing \"db/victory_objectives.txt\" file. Processing the startpos without this file will result in issues in campaign. Add the file to the pack and try again.".to_string()));
3096 }
3097 }
3098
3099 Command::BuildStarpos(pack_key, campaign_id, process_hlp_spd_data) => {
3100 let dependencies = dependencies.read().unwrap();
3101 let game_path = settings.path_buf(game.key());
3102
3103 if game.key() == KEY_THREE_KINGDOMS {
3105 match dependencies.build_starpos_pre(&mut packs, Some(&pack_key), game, &game_path, &campaign_id, process_hlp_spd_data, "historical") {
3106 Ok(_) => match dependencies.build_starpos_pre(&mut packs, Some(&pack_key), game, &game_path, &campaign_id, false, "romance") {
3107 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
3108 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
3109 }
3110 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
3111 }
3112 } else {
3113 match dependencies.build_starpos_pre(&mut packs, Some(&pack_key), game, &game_path, &campaign_id, process_hlp_spd_data, "") {
3114 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
3115 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
3116 }
3117 }
3118 }
3119
3120 Command::BuildStarposPost(pack_key, campaign_id, process_hlp_spd_data) => {
3121 let dependencies = dependencies.read().unwrap();
3122 let game_path = settings.path_buf(game.key());
3123 let asskit_path = Some(settings.path_buf(&(game.key().to_owned() + ASSEMBLY_KIT_SUFFIX)));
3124
3125 let sub_start_pos = if game.key() == KEY_THREE_KINGDOMS {
3126 vec!["historical".to_owned(), "romance".to_owned()]
3127 } else {
3128 vec![]
3129 };
3130
3131 match dependencies.build_starpos_post(&mut packs, Some(&pack_key), game, &game_path, asskit_path, &campaign_id, process_hlp_spd_data, false, &sub_start_pos) {
3132 Ok(paths) => CentralCommand::send_back(&sender, Response::VecContainerPath(paths)),
3133 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
3134 }
3135 },
3136
3137 Command::BuildStarposCleanup(pack_key, campaign_id, process_hlp_spd_data) => {
3138 let dependencies = dependencies.read().unwrap();
3139 let game_path = settings.path_buf(game.key());
3140 let asskit_path = Some(settings.path_buf(&(game.key().to_owned() + ASSEMBLY_KIT_SUFFIX)));
3141
3142 let sub_start_pos = if game.key() == KEY_THREE_KINGDOMS {
3143 vec!["historical".to_owned(), "romance".to_owned()]
3144 } else {
3145 vec![]
3146 };
3147
3148 match dependencies.build_starpos_post(&mut packs, Some(&pack_key), game, &game_path, asskit_path, &campaign_id, process_hlp_spd_data, true, &sub_start_pos) {
3149 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
3150 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
3151 }
3152 },
3153
3154 Command::BuildCeo(pack_key, akit_path, bob_exe_path) => {
3155 use std::process::Command as SysCommand;
3156 use std::time::{Duration, Instant};
3157
3158 let akit_root = PathBuf::from(&akit_path);
3159 let bob_exe = PathBuf::from(&bob_exe_path);
3160 let bob_dir = match bob_exe.parent() {
3161 Some(d) => d.to_path_buf(),
3162 None => { CentralCommand::send_back(&sender, Response::Error("Invalid BOB path".into())); continue 'background_loop; }
3163 };
3164 let raw_db = akit_root.join(r"raw_data\db");
3165 let ceo_ccd = akit_root.join(r"working_data\campaigns\ceo_data.ccd");
3166
3167 if ceo_ccd.exists() {
3169 let bak = ceo_ccd.with_extension("ccd.bak1");
3170 if let Err(e) = std::fs::copy(&ceo_ccd, &bak) {
3171 CentralCommand::send_back(&sender, Response::Error(format!("Failed to backup ceo_data.ccd: {e}")));
3172 continue 'background_loop;
3173 }
3174 }
3175
3176 let mut xml_backups: Vec<(PathBuf, PathBuf)> = Vec::new();
3178 if raw_db.exists() {
3179 match std::fs::read_dir(&raw_db) {
3180 Ok(entries) => {
3181 for entry in entries.filter_map(|e| e.ok()) {
3182 let fname = entry.file_name();
3183 let s = fname.to_string_lossy().to_lowercase();
3184 if s.starts_with("ceo") && s.ends_with(".xml") {
3185 let orig = entry.path();
3186 let bak = orig.with_extension("xml.bak");
3187 if std::fs::copy(&orig, &bak).is_ok() {
3188 xml_backups.push((orig, bak));
3189 }
3190 }
3191 }
3192 }
3193 Err(e) => {
3194 CentralCommand::send_back(&sender, Response::Error(format!("Failed to read raw_data/db: {e}")));
3195 continue 'background_loop;
3196 }
3197 }
3198 }
3199
3200 let pack_ref = match packs.get_mut(&pack_key) {
3202 Some(p) => p,
3203 None => {
3204 for (orig, bak) in &xml_backups { let _ = std::fs::rename(bak, orig); }
3205 CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {pack_key}")));
3206 continue 'background_loop;
3207 }
3208 };
3209
3210 let ceo_allowed_folders: std::collections::HashSet<&str> = [
3212 "ceo_active_permissions_tables",
3213 "ceo_anti_ceo_pairs_tables",
3214 "ceo_can_equip_requirements_tables",
3215 "ceo_categories_tables",
3216 "ceo_effect_list_to_effects_tables",
3217 "ceo_effect_lists_tables",
3218 "ceo_equipment_category_managers_tables",
3219 "ceo_equipment_manager_all_possible_ceos_tables",
3220 "ceo_equipment_manager_campaign_lookups_tables",
3221 "ceo_equipment_manager_to_category_managers_tables",
3222 "ceo_equipment_manager_types_tables",
3223 "ceo_equipment_managers_tables",
3224 "ceo_equipped_set_bonus_ceos_tables",
3225 "ceo_equipped_set_bonus_effect_bundles_tables",
3226 "ceo_equipped_set_bonuses_tables",
3227 "ceo_equipped_set_bonuses_to_incident_junctions_tables",
3228 "ceo_event_feed_categories_tables",
3229 "ceo_group_ceos_tables",
3230 "ceo_group_spawners_tables",
3231 "ceo_groups_tables",
3232 "ceo_initial_data_active_ceos_tables",
3233 "ceo_initial_data_active_spawners_tables",
3234 "ceo_initial_data_equipments_tables",
3235 "ceo_initial_data_scripted_permissions_tables",
3236 "ceo_initial_data_stages_tables",
3237 "ceo_initial_data_to_stages_tables",
3238 "ceo_initial_data_triggers_tables",
3239 "ceo_initial_datas_tables",
3240 "ceo_location_enums_tables",
3241 "ceo_nodes_tables",
3242 "ceo_permissions_groups_tables",
3243 "ceo_permissions_tables",
3244 "ceo_post_battle_loot_chances_tables",
3245 "ceo_rarities_tables",
3246 "ceo_scripted_permissions_tables",
3247 "ceo_scripted_permissions_to_permissions_tables",
3248 "ceo_set_items_tables",
3249 "ceo_sets_tables",
3250 "ceo_spawner_can_spawn_requirements_tables",
3251 "ceo_spawners_tables",
3252 "ceo_template_manager_all_possible_ceos_tables",
3253 "ceo_template_manager_campaign_lookups_tables",
3254 "ceo_template_manager_ceo_limits_tables",
3255 "ceo_template_manager_ceo_spawn_limits_tables",
3256 "ceo_template_manager_supported_categories_tables",
3257 "ceo_template_manager_types_tables",
3258 "ceo_template_managers_tables",
3259 "ceo_threshold_nodes_tables",
3260 "ceo_thresholds_tables",
3261 "ceo_to_target_ceo_junctions_tables",
3262 "ceo_to_target_factions_tables",
3263 "ceo_to_target_junction_reasons_tables",
3264 "ceo_to_target_province_junctions_tables",
3265 "ceo_to_ui_display_junctions_tables",
3266 "ceo_trigger_behaviour_enums_tables",
3267 "ceo_trigger_target_requirements_tables",
3268 "ceo_trigger_targets_tables",
3269 "ceo_trigger_to_trigger_targets_tables",
3270 "ceo_triggers_tables",
3271 "ceos_tables",
3272 "ceos_to_equipment_variants_tables",
3273 ].iter().copied().collect();
3274
3275 let ceo_table_paths: Vec<String> = pack_ref.files()
3276 .keys()
3277 .filter(|p| {
3278 let mut parts = p.splitn(3, '/');
3279 let prefix = parts.next().unwrap_or("");
3280 if prefix != "db" && prefix != "ceo_db" {
3281 return false;
3282 }
3283 parts.next()
3284 .map(|folder| ceo_allowed_folders.contains(folder))
3285 .unwrap_or(false)
3286 })
3287 .cloned()
3288 .collect();
3289
3290 if ceo_table_paths.is_empty() {
3292 for (orig, bak) in &xml_backups { let _ = std::fs::rename(bak, orig); }
3293 CentralCommand::send_back(&sender, Response::Error(
3294 "No CEO tables found in the pack (looked in db/ and ceo_db/ folders). \
3295 Import CEO tables from the Assembly Kit.".into()
3296 ));
3297 continue 'background_loop;
3298 }
3299
3300 let required_tables = [
3302 "ceos_tables",
3303 "ceo_nodes_tables",
3304 "ceo_thresholds_tables",
3305 "ceo_threshold_nodes_tables",
3306 "ceo_initial_datas_tables",
3307 ];
3308 let present_folders: std::collections::HashSet<&str> = ceo_table_paths.iter()
3309 .filter_map(|p| p.split('/').nth(1))
3310 .collect();
3311 let missing: Vec<&&str> = required_tables.iter()
3312 .filter(|t| !present_folders.contains(**t))
3313 .collect();
3314 if !missing.is_empty() {
3315 for (orig, bak) in &xml_backups { let _ = std::fs::rename(bak, orig); }
3316 let missing_list = missing.iter().map(|t| format!(" - {}", t)).collect::<Vec<_>>().join("\n");
3317 CentralCommand::send_back(&sender, Response::Error(
3318 format!("The following required CEO tables are missing from the pack:\n{}\n\n\
3319 Import them from the Assembly Kit generate them.", missing_list)
3320 ));
3321 continue 'background_loop;
3322 }
3323
3324 let decode_extra = {
3325 let mut d = DecodeableExtraData::default();
3326 d.set_schema(schema.as_ref());
3327 Some(d)
3328 };
3329 let mut export_errors: Vec<String> = Vec::new();
3330
3331 let mut xml_groups: std::collections::BTreeMap<String, Vec<String>> = std::collections::BTreeMap::new();
3334 for table_path in &ceo_table_paths {
3335 let parts: Vec<&str> = table_path.split('/').collect();
3337 if parts.len() < 2 { continue; }
3338 let folder = parts[1];
3339 let xml_name = if let Some(folder) = folder.strip_suffix("_tables") {
3340 folder.to_owned() + ".xml"
3341 } else {
3342 folder.to_owned() + ".xml"
3343 };
3344 xml_groups.entry(xml_name).or_default().push(table_path.clone());
3345 }
3346
3347 for (xml_name, table_paths) in &xml_groups {
3348 let xml_path = raw_db.join(xml_name);
3349 let table_tag = xml_name.trim_end_matches(".xml");
3350 let xsd_name = xml_name.replace(".xml", ".xsd");
3351
3352 let mut xml = format!(
3353 "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n\
3354 <dataroot xmlns:od=\"urn:schemas-microsoft-com:officedata\" \
3355 xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" \
3356 xsi:noNamespaceSchemaLocation=\"{xsd_name}\" \
3357 export_time=\"\" revision=\"0\" export_branch=\"\" export_user=\"rpfm\">\r\n\
3358 <edit_uuid>00000000-0000-0000-0000-000000000000</edit_uuid>\r\n"
3359 );
3360
3361 for table_path in table_paths {
3362 let rfile = match pack_ref.files_mut().get_mut(table_path.as_str()) {
3363 Some(f) => f,
3364 None => continue,
3365 };
3366
3367 let _ = rfile.load();
3368 let _ = rfile.decode(&decode_extra, true, false);
3369
3370 let db_table = match rfile.decoded() {
3371 Ok(RFileDecoded::DB(db)) => db.clone(),
3372 _ => { export_errors.push(format!("Could not decode {table_path}")); continue; }
3373 };
3374
3375 let fields: Vec<_> = db_table.definition().fields_processed().to_vec();
3376
3377 for row in db_table.data().iter() {
3378 let mut field_pairs: Vec<(String, String)> = Vec::new();
3379 for (field_def, value) in fields.iter().zip(row.iter()) {
3380 let fname = field_def.name().to_owned();
3381 let val_str = match value {
3382 DecodedData::Boolean(b) => if *b { "1".to_owned() } else { "0".to_owned() },
3383 DecodedData::I16(v) => v.to_string(),
3384 DecodedData::I32(v) => v.to_string(),
3385 DecodedData::I64(v) => v.to_string(),
3386 DecodedData::OptionalI16(v) => v.to_string(),
3387 DecodedData::OptionalI32(v) => v.to_string(),
3388 DecodedData::OptionalI64(v) => v.to_string(),
3389 DecodedData::F32(v) => v.to_string(),
3390 DecodedData::F64(v) => v.to_string(),
3391 DecodedData::StringU8(s) | DecodedData::StringU16(s) |
3392 DecodedData::OptionalStringU8(s) | DecodedData::OptionalStringU16(s) => s.clone(),
3393 DecodedData::ColourRGB(s) => s.clone(),
3394 _ => String::new(),
3395 };
3396 let escaped = val_str
3397 .replace('&', "&")
3398 .replace('<', "<")
3399 .replace('>', ">")
3400 .replace('"', """);
3401 field_pairs.push((fname, escaped));
3402 }
3403 field_pairs.sort_by(|a, b| a.0.cmp(&b.0));
3404
3405 xml.push_str(&format!("<{table_tag}>\r\n"));
3406 for (fname, val) in &field_pairs {
3407 xml.push_str(&format!("<{fname}>{val}</{fname}>\r\n"));
3408 }
3409 xml.push_str(&format!("</{table_tag}>\r\n"));
3410 }
3411 }
3412
3413 xml.push_str("</dataroot>\r\n");
3414
3415 if let Err(e) = std::fs::write(&xml_path, xml.as_bytes()) {
3416 export_errors.push(format!("Failed to write {xml_name}: {e}"));
3417 }
3418 }
3419
3420 if !export_errors.is_empty() {
3421 for (orig, bak) in &xml_backups { let _ = std::fs::rename(bak, orig); }
3422 CentralCommand::send_back(&sender, Response::Error(format!("Export errors:\n{}", export_errors.join("\n"))));
3423 continue 'background_loop;
3424 }
3425
3426 let cfg_path = bob_dir.join("BOB/default_configuration.xml");
3428
3429 let cfg_backup = bob_dir.join("BOB/default_configuration.xml.rpfm_bak");
3431 let cfg_existed = cfg_path.exists();
3432 if cfg_existed {
3433 if let Err(e) = std::fs::rename(&cfg_path, &cfg_backup) {
3434 for (orig, bak) in &xml_backups { let _ = std::fs::rename(bak, orig); }
3435 CentralCommand::send_back(&sender, Response::Error(format!("Failed to backup BOB config: {e}")));
3436 continue 'background_loop;
3437 }
3438 }
3439
3440 const BOB_CONFIG_XML: &str = r#"<?xml version="1.0" encoding="UTF-8"?>
3441 <bob_configuration><processors><processor>Campaign</processor></processors>
3442 <directories/><global_rules/><retail>0</retail><silent>1</silent>
3443 <get_latest>0</get_latest><connect_db>0</connect_db>
3444 <merge_for_checkin_mode>2</merge_for_checkin_mode>
3445 <selected_files><entry><working>/campaigns/ceo_data.ccd</entry></selected_files>
3446 </bob_configuration>"#;
3447
3448 if let Err(e) = std::fs::write(&cfg_path, BOB_CONFIG_XML) {
3449 if cfg_existed { let _ = std::fs::rename(&cfg_backup, &cfg_path); }
3451 for (orig, bak) in &xml_backups { let _ = std::fs::rename(bak, orig); }
3452 CentralCommand::send_back(&sender, Response::Error(format!("Failed to write BOB config: {e}")));
3453 continue 'background_loop;
3454 }
3455
3456 let output = match SysCommand::new(&bob_exe).current_dir(&bob_dir).output() {
3457 Ok(o) => o,
3458 Err(e) => {
3459 let _ = std::fs::remove_file(&cfg_path);
3460 if cfg_existed { let _ = std::fs::rename(&cfg_backup, &cfg_path); }
3461 for (orig, bak) in &xml_backups { let _ = std::fs::rename(bak, orig); }
3462 CentralCommand::send_back(&sender, Response::Error(format!("Failed to launch BOB: {e}")));
3463 continue 'background_loop;
3464 }
3465 };
3466
3467 let _ = std::fs::remove_file(&cfg_path);
3469 if cfg_existed { let _ = std::fs::rename(&cfg_backup, &cfg_path); }
3470
3471 let deadline = Instant::now() + Duration::from_secs(180);
3473 let found = loop {
3474 if ceo_ccd.exists() { break true; }
3475 if Instant::now() >= deadline { break false; }
3476 std::thread::sleep(Duration::from_millis(500));
3477 };
3478
3479 for (orig, bak) in &xml_backups {
3481 let _ = std::fs::rename(bak, orig);
3482 }
3483
3484 if found {
3486 CentralCommand::send_back(&sender, Response::Success);
3487 } else {
3488 CentralCommand::send_back(&sender, Response::Error(format!(
3489 "ceo_data.ccd not generated within 180s. BOB exit: {:?}\nstdout: {}",
3490 output.status.code(),
3491 String::from_utf8_lossy(&output.stdout)
3492 )));
3493 }
3494 }
3495
3496 Command::BuildCeoPost(pack_key, akit_path) => {
3497 match packs.get_mut(&pack_key) {
3498 Some(pack) => {
3499 match build_ceo_post(pack, &akit_path) {
3500 Ok(paths) => CentralCommand::send_back(&sender, Response::VecContainerPath(paths)),
3501 Err(e) => CentralCommand::send_back(&sender, Response::Error(e.to_string())),
3502 }
3503 }
3504 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {pack_key}"))),
3505 }
3506 }
3507
3508 Command::GetTraitCeos => {
3509 let deps = dependencies.read().unwrap();
3510 let trait_ceos = get_trait_ceos(&deps);
3511 CentralCommand::send_back(&sender, Response::VecStringTuples(trait_ceos));
3512 }
3513
3514 Command::BuildCeoEntries(pack_key, entries) => {
3515 let result = (|| -> Result<Vec<ContainerPath>> {
3516 let schema = schema.as_ref()
3517 .ok_or_else(|| anyhow!("No schema loaded for the current game."))?;
3518 let pack = packs.get_mut(&pack_key)
3519 .ok_or_else(|| anyhow!("Pack not found: {}", pack_key))?;
3520 build_ceo_entries(pack, schema, &entries)
3521 })();
3522
3523 match result {
3524 Ok(paths) => CentralCommand::send_back(&sender, Response::VecContainerPath(paths)),
3525 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
3526 }
3527 }
3528
3529 Command::UpdateAnimIds(pack_key, starting_id, offset) => {
3530 match packs.get_mut(&pack_key) {
3531 Some(pack) => {
3532 match pack.update_anim_ids(game, starting_id, offset) {
3533 Ok(paths) => CentralCommand::send_back(&sender, Response::VecContainerPath(paths)),
3534 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
3535 }
3536 }
3537 None => CentralCommand::send_back(&sender, Response::Error(format!("Pack not found: {}", pack_key))),
3538 }
3539 }
3540
3541 Command::GetTablesFromDependencies(table_name) => {
3542 let dependencies = dependencies.read().unwrap();
3543 match dependencies.db_data(&table_name, true, true) {
3544 Ok(files) => CentralCommand::send_back(&sender, Response::VecRFile(files.iter().map(|x| (**x).clone()).collect::<Vec<_>>())),
3545 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
3546 }
3547 }
3548
3549 Command::ExportRigidToGltf(rigid, path) => {
3550 let mut dependencies = dependencies.write().unwrap();
3551 match gltf_from_rigid(&rigid, &mut dependencies) {
3552 Ok(gltf) => match save_gltf_to_disk(&gltf, &PathBuf::from(path)) {
3553 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
3554 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
3555 },
3556 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
3557 }
3558 }
3559
3560 Command::SettingsGetBool(key) => {
3562 CentralCommand::send_back(&sender, Response::Bool(settings.bool(&key)));
3563 }
3564 Command::SettingsGetI32(key) => {
3565 CentralCommand::send_back(&sender, Response::I32(settings.i32(&key)));
3566 }
3567 Command::SettingsGetF32(key) => {
3568 CentralCommand::send_back(&sender, Response::F32(settings.f32(&key)));
3569 }
3570 Command::SettingsGetString(key) => {
3571 CentralCommand::send_back(&sender, Response::String(settings.string(&key)));
3572 }
3573 Command::SettingsGetPathBuf(key) => {
3574 CentralCommand::send_back(&sender, Response::PathBuf(settings.path_buf(&key)));
3575 }
3576 Command::SettingsGetVecString(key) => {
3577 CentralCommand::send_back(&sender, Response::VecString(settings.vec_string(&key)));
3578 }
3579 Command::SettingsGetVecRaw(key) => {
3580 CentralCommand::send_back(&sender, Response::VecU8(settings.raw_data(&key)));
3581 }
3582 Command::SettingsGetAll => {
3583 CentralCommand::send_back(&sender, Response::SettingsAll(SettingsSnapshot {
3584 bool: settings.bool.clone(),
3585 i32: settings.i32.clone(),
3586 f32: settings.f32.clone(),
3587 string: settings.string.clone(),
3588 raw_data: settings.raw_data.clone(),
3589 vec_string: settings.vec_string.clone(),
3590 }));
3591 }
3592 Command::SettingsSetBool(key, value) => {
3593 match settings.set_bool(&key, value) {
3594 Ok(_) => {
3595 match key.as_str() {
3596 ENABLE_USAGE_TELEMETRY => rpfm_telemetry::set_usage_telemetry_enabled(value),
3597 ENABLE_CRASH_REPORTS => rpfm_telemetry::set_crash_reports_enabled(value),
3598 _ => {}
3599 }
3600 CentralCommand::send_back(&sender, Response::Success);
3601 }
3602 Err(e) => CentralCommand::send_back(&sender, Response::Error(e.to_string())),
3603 }
3604 }
3605 Command::SettingsSetI32(key, value) => {
3606 match settings.set_i32(&key, value) {
3607 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
3608 Err(e) => CentralCommand::send_back(&sender, Response::Error(e.to_string())),
3609 }
3610 }
3611 Command::SettingsSetF32(key, value) => {
3612 match settings.set_f32(&key, value) {
3613 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
3614 Err(e) => CentralCommand::send_back(&sender, Response::Error(e.to_string())),
3615 }
3616 }
3617 Command::SettingsSetString(key, value) => {
3618 match settings.set_string(&key, &value) {
3619 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
3620 Err(e) => CentralCommand::send_back(&sender, Response::Error(e.to_string())),
3621 }
3622 }
3623 Command::SettingsSetPathBuf(key, value) => {
3624 match settings.set_path_buf(&key, &value) {
3625 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
3626 Err(e) => CentralCommand::send_back(&sender, Response::Error(e.to_string())),
3627 }
3628 }
3629 Command::SettingsSetVecString(key, value) => {
3630 match settings.set_vec_string(&key, &value) {
3631 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
3632 Err(e) => CentralCommand::send_back(&sender, Response::Error(e.to_string())),
3633 }
3634 }
3635 Command::SettingsSetVecRaw(key, value) => {
3636 match settings.set_raw_data(&key, &value) {
3637 Ok(_) => CentralCommand::send_back(&sender, Response::Success),
3638 Err(e) => CentralCommand::send_back(&sender, Response::Error(e.to_string())),
3639 }
3640 },
3641 Command::ConfigPath => {
3642 match config_path() {
3643 Ok(path) => CentralCommand::send_back(&sender, Response::PathBuf(path)),
3644 Err(e) => CentralCommand::send_back(&sender, Response::Error(e.to_string())),
3645 }
3646 },
3647 Command::AssemblyKitPath => {
3648 match settings.assembly_kit_path(game) {
3649 Ok(path) => CentralCommand::send_back(&sender, Response::PathBuf(path)),
3650 Err(e) => CentralCommand::send_back(&sender, Response::Error(e.to_string())),
3651 }
3652 },
3653 Command::BackupAutosavePath => {
3654 match backup_autosave_path() {
3655 Ok(path) => CentralCommand::send_back(&sender, Response::PathBuf(path)),
3656 Err(e) => CentralCommand::send_back(&sender, Response::Error(e.to_string())),
3657 }
3658 },
3659 Command::OldAkDataPath => {
3660 match old_ak_files_path() {
3661 Ok(path) => CentralCommand::send_back(&sender, Response::PathBuf(path)),
3662 Err(e) => CentralCommand::send_back(&sender, Response::Error(e.to_string())),
3663 }
3664 },
3665 Command::SchemasPath => {
3666 match schemas_path() {
3667 Ok(path) => CentralCommand::send_back(&sender, Response::PathBuf(path)),
3668 Err(e) => CentralCommand::send_back(&sender, Response::Error(e.to_string())),
3669 }
3670 },
3671 Command::TableProfilesPath => {
3672 match table_profiles_path() {
3673 Ok(path) => CentralCommand::send_back(&sender, Response::PathBuf(path)),
3674 Err(e) => CentralCommand::send_back(&sender, Response::Error(e.to_string())),
3675 }
3676 },
3677 Command::TranslationsLocalPath => {
3678 match translations_local_path() {
3679 Ok(path) => CentralCommand::send_back(&sender, Response::PathBuf(path)),
3680 Err(e) => CentralCommand::send_back(&sender, Response::Error(e.to_string())),
3681 }
3682 },
3683 Command::DependenciesCachePath => {
3684 match dependencies_cache_path() {
3685 Ok(path) => CentralCommand::send_back(&sender, Response::PathBuf(path)),
3686 Err(e) => CentralCommand::send_back(&sender, Response::Error(e.to_string())),
3687 }
3688 },
3689 Command::SettingsClearPath(path) => {
3690 match clear_config_path(&path) {
3691 Ok(()) => CentralCommand::send_back(&sender, Response::Success),
3692 Err(e) => CentralCommand::send_back(&sender, Response::Error(e.to_string())),
3693 }
3694 },
3695 Command::BackupSettings => {
3696 backup_settings = settings.clone();
3697 CentralCommand::send_back(&sender, Response::Success);
3698 }
3699 Command::ClearSettings => match Settings::init(true) {
3700 Ok(set) => {
3701 settings = set;
3702 CentralCommand::send_back(&sender, Response::Success);},
3703 Err(e) => CentralCommand::send_back(&sender, Response::Error(e.to_string())),
3704 },
3705 Command::RestoreBackupSettings => {
3706 settings = backup_settings.clone();
3707 CentralCommand::send_back(&sender, Response::Success);
3708 }
3709 Command::OptimizerOptions => CentralCommand::send_back(&sender, Response::OptimizerOptions(settings.optimizer_options())),
3710
3711 Command::IsSchemaLoaded => CentralCommand::send_back(&sender, Response::Bool(schema.is_some())),
3712 Command::DefinitionsByTableName(name) => match schema {
3713 Some(ref schema) => {
3714 match schema.definitions_by_table_name(&name) {
3715 Some(defs) => CentralCommand::send_back(&sender, Response::VecDefinition(defs.to_vec())),
3716 None => CentralCommand::send_back(&sender, Response::VecDefinition(vec![])),
3717 }
3718 },
3719 None => CentralCommand::send_back(&sender, Response::Error(anyhow!("There is no Schema for the Game Selected.").to_string())),
3720 },
3721 Command::ReferencingColumnsForDefinition(name, definition) => match schema {
3722 Some(ref schema) => CentralCommand::send_back(&sender, Response::HashMapStringHashMapStringVecString(schema.referencing_columns_for_table(&name, &definition))),
3723 None => CentralCommand::send_back(&sender, Response::Error("There is no Schema for the Game Selected.".to_string())),
3724 },
3725 Command::Schema => match &schema {
3726 Some(schema) => CentralCommand::send_back(&sender, Response::Schema(schema.clone())),
3727 None => CentralCommand::send_back(&sender, Response::Error("There is no Schema for the Game Selected.".to_string())),
3728 }
3729 Command::DefinitionByTableNameAndVersion(name, version) => match schema {
3730 Some(ref schema) => match schema.definition_by_name_and_version(&name, version) {
3731 Some(def) => CentralCommand::send_back(&sender, Response::Definition(def.clone())),
3732 None => CentralCommand::send_back(&sender, Response::Error(format!("No definition found for table '{}' with version {}.", name, version))),
3733 },
3734 None => CentralCommand::send_back(&sender, Response::Error("There is no Schema for the Game Selected.".to_string())),
3735 },
3736
3737 Command::DeleteDefinition(name, version) => {
3738 if let Some(ref mut schema) = schema {
3739 schema.remove_definition(&name, version);
3740 }
3741 CentralCommand::send_back(&sender, Response::Success);
3742 }
3743
3744 Command::FieldsProcessed(definition) => {
3745 CentralCommand::send_back(&sender, Response::VecField(definition.fields_processed()));
3746 }
3747 }
3748 }
3749}
3750
3751fn load_schema(schema: &mut Option<Schema>, packs: &mut BTreeMap<String, Pack>, game: &GameInfo, settings: &Settings) {
3753
3754 for pack in packs.values_mut() {
3756 let cf = pack.compression_format();
3757 let mut files = pack.files_by_type_mut(&[FileType::DB]);
3758 let extra_data = Some(EncodeableExtraData::new_from_game_info_and_settings(game, cf, settings.bool("disable_uuid_regeneration_on_db_tables")));
3759
3760 files.par_iter_mut().for_each(|file| {
3761 let _ = file.encode(&extra_data, true, true, false);
3762 });
3763 }
3764
3765 let schema_path = schemas_path().unwrap().join(game.schema_file_name());
3767 let local_patches_path = table_patches_path().unwrap().join(game.schema_file_name());
3768 *schema = Schema::load(&schema_path, Some(&local_patches_path)).ok();
3769
3770 if let Some(ref schema) = schema {
3772 for pack in packs.values_mut() {
3773 let mut files = pack.files_by_type_mut(&[FileType::DB]);
3774 let mut extra_data = DecodeableExtraData::default();
3775 extra_data.set_schema(Some(schema));
3776 let extra_data = Some(extra_data);
3777
3778 files.par_iter_mut().for_each(|file| {
3779 let _ = file.decode(&extra_data, true, false);
3780 });
3781 }
3782 }
3783}
3784
3785fn decode_and_send_file(file: &mut RFile, sender: &UnboundedSender<Response>, settings: &Settings, game: &GameInfo, schema: &Option<Schema>) {
3786 let mut extra_data = DecodeableExtraData::default();
3787 extra_data.set_schema(schema.as_ref());
3788 extra_data.set_game_info(Some(game));
3789
3790 let mut ignored_file_types = vec![
3792 FileType::Anim,
3793 FileType::BMD,
3794 FileType::BMDVegetation,
3795 FileType::Dat,
3796 FileType::Font,
3797 FileType::HlslCompiled,
3798 FileType::Pack,
3799 FileType::SoundBank,
3800 FileType::Unknown
3801 ];
3802
3803 if !settings.bool("enable_esf_editor") {
3805 ignored_file_types.push(FileType::ESF);
3806 }
3807
3808 if ignored_file_types.contains(&file.file_type()) {
3809 return CentralCommand::send_back(sender, Response::Unknown);
3810 }
3811 let result = file.decode(&Some(extra_data), true, true).transpose().unwrap();
3812
3813 match result {
3814 Ok(RFileDecoded::AnimFragmentBattle(data)) => CentralCommand::send_back(sender, Response::AnimFragmentBattleRFileInfo(data, From::from(&*file))),
3815 Ok(RFileDecoded::AnimPack(data)) => CentralCommand::send_back(sender, Response::AnimPackRFileInfo(data.files().values().map(From::from).collect(), From::from(&*file))),
3816 Ok(RFileDecoded::AnimsTable(data)) => CentralCommand::send_back(sender, Response::AnimsTableRFileInfo(data, From::from(&*file))),
3817 Ok(RFileDecoded::Anim(_)) => CentralCommand::send_back(sender, Response::Unknown),
3818 Ok(RFileDecoded::Atlas(data)) => CentralCommand::send_back(sender, Response::AtlasRFileInfo(data, From::from(&*file))),
3819 Ok(RFileDecoded::Audio(data)) => CentralCommand::send_back(sender, Response::AudioRFileInfo(data, From::from(&*file))),
3820 Ok(RFileDecoded::BMD(_)) => CentralCommand::send_back(sender, Response::Unknown),
3821 Ok(RFileDecoded::BMDVegetation(_)) => CentralCommand::send_back(sender, Response::Unknown),
3822 Ok(RFileDecoded::Dat(_)) => CentralCommand::send_back(sender, Response::Unknown),
3823 Ok(RFileDecoded::DB(table)) => CentralCommand::send_back(sender, Response::DBRFileInfo(table, From::from(&*file))),
3824 Ok(RFileDecoded::ESF(data)) => CentralCommand::send_back(sender, Response::ESFRFileInfo(data, From::from(&*file))),
3825 Ok(RFileDecoded::Font(_)) => CentralCommand::send_back(sender, Response::Unknown),
3826 Ok(RFileDecoded::HlslCompiled(_)) => CentralCommand::send_back(sender, Response::Unknown),
3827 Ok(RFileDecoded::GroupFormations(data)) => CentralCommand::send_back(sender, Response::GroupFormationsRFileInfo(data, From::from(&*file))),
3828 Ok(RFileDecoded::Image(image)) => CentralCommand::send_back(sender, Response::ImageRFileInfo(image, From::from(&*file))),
3829 Ok(RFileDecoded::Loc(table)) => CentralCommand::send_back(sender, Response::LocRFileInfo(table, From::from(&*file))),
3830 Ok(RFileDecoded::MatchedCombat(data)) => CentralCommand::send_back(sender, Response::MatchedCombatRFileInfo(data, From::from(&*file))),
3831 Ok(RFileDecoded::Pack(_)) => CentralCommand::send_back(sender, Response::Unknown),
3832 Ok(RFileDecoded::PortraitSettings(data)) => CentralCommand::send_back(sender, Response::PortraitSettingsRFileInfo(data, From::from(&*file))),
3833 Ok(RFileDecoded::RigidModel(data)) => CentralCommand::send_back(sender, Response::RigidModelRFileInfo(data, From::from(&*file))),
3834 Ok(RFileDecoded::SoundBank(_)) => CentralCommand::send_back(sender, Response::Unknown),
3835 Ok(RFileDecoded::Text(text)) => CentralCommand::send_back(sender, Response::TextRFileInfo(text, From::from(&*file))),
3836 Ok(RFileDecoded::UIC(uic)) => CentralCommand::send_back(sender, Response::UICRFileInfo(uic, From::from(&*file))),
3837 Ok(RFileDecoded::UnitVariant(data)) => CentralCommand::send_back(sender, Response::UnitVariantRFileInfo(data, From::from(&*file))),
3838 Ok(RFileDecoded::Unknown(_)) => CentralCommand::send_back(sender, Response::Unknown),
3839 Ok(RFileDecoded::Video(data)) => CentralCommand::send_back(sender, Response::VideoInfoRFileInfo(From::from(&data), From::from(&*file))),
3840 Ok(RFileDecoded::VMD(data)) => CentralCommand::send_back(sender, Response::VMDRFileInfo(data, From::from(&*file))),
3841 Ok(RFileDecoded::WSModel(data)) => CentralCommand::send_back(sender, Response::WSModelRFileInfo(data, From::from(&*file))),
3842 Err(error) => CentralCommand::send_back(sender, Response::Error(error.to_string())),
3843 }
3844}
3845
3846fn exe_path() -> PathBuf {
3849 if cfg!(debug_assertions) {
3850 std::env::current_dir().unwrap()
3851 } else {
3852 let mut path = std::env::current_exe().unwrap();
3853 path.pop();
3854 path
3855 }
3856}
3857
3858fn git_update_check(
3861 sender: UnboundedSender<Response>,
3862 path_fn: fn() -> Result<PathBuf>,
3863 repo: &'static str,
3864 branch: &'static str,
3865 remote: &'static str,
3866) {
3867 tokio::spawn(async move {
3868 let result = tokio::task::spawn_blocking(move || {
3869 match path_fn() {
3870 Ok(local_path) => {
3871 let git_integration = GitIntegration::new(&local_path, repo, branch, remote);
3872 git_integration.check_update().map_err(|e| e.into())
3873 }
3874 Err(error) => Err(error),
3875 }
3876 }).await.unwrap();
3877
3878 match result {
3879 Ok(response) => CentralCommand::send_back(&sender, Response::APIResponseGit(response)),
3880 Err(error) => CentralCommand::send_back(&sender, Response::Error(error.to_string())),
3881 }
3882 });
3883}
3884
3885fn tr(s: &str) -> String {
3887 s.to_owned()
3888}