1use directories::ProjectDirs;
14use getset::*;
15#[cfg(feature = "integration_log")] use log::{info, warn};
16use steamlocate::SteamDir;
17
18use std::collections::HashMap;
19use std::{fmt, fmt::Display};
20use std::fs::{DirBuilder, File};
21use std::io::{BufReader, Read};
22use std::path::{Path, PathBuf};
23use std::sync::{Arc, RwLock};
24
25use crate::compression::CompressionFormat;
26use crate::error::{RLibError, Result};
27use crate::utils::*;
28
29use self::supported_games::*;
30use self::manifest::Manifest;
31use self::pfh_file_type::PFHFileType;
32use self::pfh_version::PFHVersion;
33
34pub mod supported_games;
35pub mod manifest;
36pub mod pfh_file_type;
37pub mod pfh_version;
38
39pub const BRAZILIAN: &str = "br";
40pub const SIMPLIFIED_CHINESE: &str = "cn";
41pub const CZECH: &str = "cz";
42pub const ENGLISH: &str = "en";
43pub const FRENCH: &str = "fr";
44pub const GERMAN: &str = "ge";
45pub const ITALIAN: &str = "it";
46pub const KOREAN: &str = "kr";
47pub const POLISH: &str = "pl";
48pub const RUSSIAN: &str = "ru";
49pub const SPANISH: &str = "sp";
50pub const TURKISH: &str = "tr";
51pub const TRADITIONAL_CHINESE: &str = "zh";
52
53pub const LUA_AUTOGEN_FOLDER: &str = "tw_autogen";
54pub const LUA_REPO: &str = "https://github.com/chadvandy/tw_autogen";
55pub const LUA_REMOTE: &str = "origin";
56pub const LUA_BRANCH: &str = "main";
57
58pub const OLD_AK_REPO: &str = "https://github.com/Frodo45127/total_war_ak_files_pre_shogun_2";
59pub const OLD_AK_REMOTE: &str = "origin";
60pub const OLD_AK_BRANCH: &str = "master";
61
62pub const TRANSLATIONS_REPO: &str = "https://github.com/Frodo45127/total_war_translation_hub";
63pub const TRANSLATIONS_REMOTE: &str = "origin";
64pub const TRANSLATIONS_BRANCH: &str = "master";
65
66#[derive(Getters, Clone, Debug)]
72#[getset(get = "pub")]
73pub struct GameInfo {
74
75 #[getset(skip)]
77 key: &'static str,
78
79 display_name: &'static str,
81
82 pfh_versions: HashMap<PFHFileType, PFHVersion>,
85
86 schema_file_name: String,
88
89 dependencies_cache_file_name: String,
91
92 raw_db_version: i16,
94
95 portrait_settings_version: Option<u32>,
97
98 supports_editing: bool,
100
101 db_tables_have_guid: bool,
103
104 locale_file_name: Option<String>,
106
107 banned_packedfiles: Vec<String>,
109
110 icon_small: String,
112
113 icon_big: String,
115
116 vanilla_db_table_name_logic: VanillaDBTableNameLogic,
118
119 #[getset(skip)]
121 install_data: HashMap<InstallType, InstallData>,
122
123 tool_vars: HashMap<String, String>,
125
126 lua_autogen_folder: Option<String>,
128
129 ak_lost_fields: Vec<String>,
131
132 #[getset(skip)]
134 install_type_cache: Arc<RwLock<HashMap<PathBuf, InstallType>>>,
135
136 compression_formats_supported: Vec<CompressionFormat>
138}
139
140#[derive(Clone, Debug)]
142pub enum VanillaDBTableNameLogic {
143
144 FolderName,
146
147 DefaultName(String),
149}
150
151#[derive(Clone, Debug, Hash, PartialEq, Eq)]
153pub enum InstallType {
154
155 WinSteam,
157
158 LnxSteam,
160
161 WinEpic,
163
164 WinWargaming,
166}
167
168#[derive(Getters, Clone, Debug)]
172#[getset(get = "pub")]
173pub struct InstallData {
174
175 vanilla_packs: Vec<String>,
178
179 use_manifest: bool,
181
182 store_id: u64,
184
185 store_id_ak: u64,
187
188 executable: String,
190
191 data_path: String,
193
194 language_path: String,
196
197 local_mods_path: String,
199
200 downloaded_mods_path: String,
202
203 config_folder: Option<String>,
205}
206
207impl Display for InstallType {
212 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
213 Display::fmt(match self {
214 Self::WinSteam => "Windows - Steam",
215 Self::LnxSteam => "Linux - Steam",
216 Self::WinEpic => "Windows - Epic",
217 Self::WinWargaming => "Windows - Wargaming",
218 }, f)
219 }
220}
221
222impl GameInfo {
224
225 pub fn key(&self) -> &str {
231 self.key
232 }
233
234 pub fn pfh_version_by_file_type(&self, pfh_file_type: PFHFileType) -> PFHVersion {
236 match self.pfh_versions.get(&pfh_file_type) {
237 Some(pfh_version) => *pfh_version,
238 None => *self.pfh_versions.get(&PFHFileType::Mod).unwrap(),
239 }
240 }
241
242 pub fn install_type(&self, game_path: &Path) -> Result<InstallType> {
248
249 if let Some(install_type) = self.install_type_cache.read().unwrap().get(game_path) {
252 return Ok(install_type.clone());
253 }
254
255 let base_path_files = files_from_subdir(game_path, false)?;
257 let install_type_by_exe = self.install_data.iter().filter_map(|(install_type, install_data)|
258 if base_path_files.iter().filter_map(|path| if path.is_file() { path.file_name() } else { None }).any(|filename| filename == &**install_data.executable()) {
259 Some(install_type)
260 } else { None }
261 ).collect::<Vec<&InstallType>>();
262
263 if install_type_by_exe.is_empty() {
265 let install_type = self.install_data.keys().next().unwrap();
266 self.install_type_cache.write().unwrap().insert(game_path.to_path_buf(), install_type.clone());
267 Ok(install_type.clone())
268 }
269
270 else if install_type_by_exe.len() == 1 {
272 self.install_type_cache.write().unwrap().insert(game_path.to_path_buf(), install_type_by_exe[0].clone());
273 Ok(install_type_by_exe[0].clone())
274 }
275
276 else {
278
279 let is_windows = install_type_by_exe.iter().any(|install_type| install_type == &&InstallType::WinSteam || install_type == &&InstallType::WinEpic || install_type == &&InstallType::WinWargaming);
282 if is_windows {
283
284 let has_steam_api_dll = base_path_files.iter().filter_map(|path| path.file_name()).any(|filename| filename == "steam_api.dll" || filename == "steam_api64.dll");
286 let has_eos_sdk_dll = base_path_files.iter().filter_map(|path| path.file_name()).any(|filename| filename == "EOSSDK-Win64-Shipping.dll");
287 if has_steam_api_dll && install_type_by_exe.contains(&&InstallType::WinSteam) {
288 self.install_type_cache.write().unwrap().insert(game_path.to_path_buf(), InstallType::WinSteam);
289 Ok(InstallType::WinSteam)
290 }
291
292 else if has_eos_sdk_dll && install_type_by_exe.contains(&&InstallType::WinEpic) {
294 self.install_type_cache.write().unwrap().insert(game_path.to_path_buf(), InstallType::WinEpic);
295 Ok(InstallType::WinEpic)
296 }
297
298 else {
300 self.install_type_cache.write().unwrap().insert(game_path.to_path_buf(), InstallType::WinWargaming);
301 Ok(InstallType::WinWargaming)
302 }
303 }
304
305 else {
307 self.install_type_cache.write().unwrap().insert(game_path.to_path_buf(), InstallType::LnxSteam);
308 Ok(InstallType::LnxSteam)
309 }
310 }
311 }
312
313 pub fn install_data(&self, game_path: &Path) -> Result<&InstallData> {
315 let install_type = self.install_type(game_path)?;
316 let install_data = self.install_data.get(&install_type).ok_or_else(|| RLibError::GameInstallTypeNotSupported(self.display_name.to_string(), install_type.to_string()))?;
317 Ok(install_data)
318 }
319
320 pub fn data_path(&self, game_path: &Path) -> Result<PathBuf> {
322 let install_type = self.install_type(game_path)?;
323 let install_data = self.install_data.get(&install_type).ok_or_else(|| RLibError::GameInstallTypeNotSupported(self.display_name.to_string(), install_type.to_string()))?;
324 Ok(game_path.join(install_data.data_path()))
325 }
326
327 pub fn content_path(&self, game_path: &Path) -> Result<PathBuf> {
329 let install_type = self.install_type(game_path)?;
330 let install_data = self.install_data.get(&install_type).ok_or_else(|| RLibError::GameInstallTypeNotSupported(self.display_name.to_string(), install_type.to_string()))?;
331 Ok(game_path.join(install_data.downloaded_mods_path()))
332 }
333
334 pub fn language_path(&self, game_path: &Path) -> Result<PathBuf> {
336
337 let language_file_name = self.locale_file_name().clone().unwrap_or_else(|| "language.txt".to_owned());
339
340 let install_type = self.install_type(game_path)?;
341 let install_data = self.install_data.get(&install_type).ok_or_else(|| RLibError::GameInstallTypeNotSupported(self.display_name.to_string(), install_type.to_string()))?;
342 let base_path = game_path.join(install_data.language_path());
343
344 let path_with_file = base_path.join(language_file_name);
346 if path_with_file.is_file() {
347 Ok(base_path)
348 } else {
349
350 let path = base_path.join(BRAZILIAN);
352 if path.is_dir() {
353 return Ok(path);
354 }
355 let path = base_path.join(SIMPLIFIED_CHINESE);
356 if path.is_dir() {
357 return Ok(path);
358 }
359 let path = base_path.join(CZECH);
360 if path.is_dir() {
361 return Ok(path);
362 }
363 let path = base_path.join(ENGLISH);
364 if path.is_dir() {
365 return Ok(path);
366 }
367 let path = base_path.join(FRENCH);
368 if path.is_dir() {
369 return Ok(path);
370 }
371 let path = base_path.join(GERMAN);
372 if path.is_dir() {
373 return Ok(path);
374 }
375 let path = base_path.join(ITALIAN);
376 if path.is_dir() {
377 return Ok(path);
378 }
379 let path = base_path.join(KOREAN);
380 if path.is_dir() {
381 return Ok(path);
382 }
383 let path = base_path.join(POLISH);
384 if path.is_dir() {
385 return Ok(path);
386 }
387 let path = base_path.join(RUSSIAN);
388 if path.is_dir() {
389 return Ok(path);
390 }
391 let path = base_path.join(SPANISH);
392 if path.is_dir() {
393 return Ok(path);
394 }
395 let path = base_path.join(TURKISH);
396 if path.is_dir() {
397 return Ok(path);
398 }
399 let path = base_path.join(TRADITIONAL_CHINESE);
400 if path.is_dir() {
401 return Ok(path);
402 }
403
404 Ok(base_path)
406 }
407 }
408
409 pub fn local_mods_path(&self, game_path: &Path) -> Result<PathBuf> {
411 let install_type = self.install_type(game_path)?;
412 let install_data = self.install_data.get(&install_type).ok_or_else(|| RLibError::GameInstallTypeNotSupported(self.display_name.to_string(), install_type.to_string()))?;
413 Ok(game_path.join(install_data.local_mods_path()))
414 }
415
416 pub fn content_packs_paths(&self, game_path: &Path) -> Option<Vec<PathBuf>> {
418 let install_type = self.install_type(game_path).ok()?;
419 let install_data = self.install_data.get(&install_type)?;
420 let downloaded_mods_path = install_data.downloaded_mods_path();
421
422 if downloaded_mods_path.is_empty() {
424 return None;
425 }
426
427 let path = std::fs::canonicalize(game_path.join(downloaded_mods_path)).ok()?;
428 let mut paths = vec![];
429
430 for path in files_from_subdir(&path, true).ok()?.iter() {
431 match path.extension() {
432 Some(extension) => if extension == "pack" || extension == "bin" { paths.push(path.to_path_buf()); }
433 None => continue,
434 }
435 }
436
437 paths.sort();
438 Some(paths)
439 }
440
441 pub fn secondary_packs_paths(&self, secondary_path: &Path) -> Option<Vec<PathBuf>> {
445 if !secondary_path.is_dir() || !secondary_path.exists() || !secondary_path.is_absolute() {
446 return None;
447 }
448
449 let game_path = secondary_path.join(self.key());
450 if !game_path.is_dir() || !game_path.exists() {
451 return None;
452 }
453
454 let mut paths = vec![];
455
456 for path in files_from_subdir(&game_path, false).ok()?.iter() {
457 match path.extension() {
458 Some(extension) => if extension == "pack" {
459 paths.push(path.to_path_buf());
460 }
461 None => continue,
462 }
463 }
464
465 paths.sort();
466 Some(paths)
467 }
468
469 pub fn data_packs_paths(&self, game_path: &Path) -> Option<Vec<PathBuf>> {
471 let game_path = self.data_path(game_path).ok()?;
472 let mut paths = vec![];
473
474 for path in files_from_subdir(&game_path, false).ok()?.iter() {
475 match path.extension() {
476 Some(extension) => if extension == "pack" { paths.push(path.to_path_buf()); }
477 None => continue,
478 }
479 }
480
481 paths.sort();
482 Some(paths)
483 }
484
485
486 pub fn mymod_install_path(&self, game_path: &Path) -> Option<PathBuf> {
488 let install_type = self.install_type(game_path).ok()?;
489 let install_data = self.install_data.get(&install_type)?;
490 let path = game_path.join(PathBuf::from(install_data.local_mods_path()));
491
492 DirBuilder::new().recursive(true).create(&path).ok()?;
494
495 Some(path)
496 }
497
498 pub fn use_manifest(&self, game_path: &Path) -> Result<bool> {
500 let install_type = self.install_type(game_path)?;
501 let install_data = self.install_data.get(&install_type).ok_or_else(|| RLibError::GameInstallTypeNotSupported(self.display_name.to_string(), install_type.to_string()))?;
502
503 Ok(*install_data.use_manifest())
505 }
506
507 pub fn steam_id(&self, game_path: &Path) -> Result<u64> {
509 let install_type = self.install_type(game_path)?;
510 let install_data = match install_type {
511 InstallType::WinSteam |
512 InstallType::LnxSteam => self.install_data.get(&install_type).ok_or_else(|| RLibError::GameInstallTypeNotSupported(self.display_name.to_string(), install_type.to_string()))?,
513 _ => return Err(RLibError::ReservedFiles)
514 };
515
516 Ok(*install_data.store_id())
517 }
518
519 pub fn ca_packs_paths(&self, game_path: &Path) -> Result<Vec<PathBuf>> {
525
526 let language = self.game_locale_from_file(game_path)?;
528
529 if !self.use_manifest(game_path)? {
531 self.ca_packs_paths_no_manifest(game_path, &language)
532 } else {
533
534 match Manifest::read_from_game_path(self, game_path) {
536 Ok(manifest) => {
537 let data_path = self.data_path(game_path)?;
538 let mut paths = manifest.0.iter().filter_map(|entry|
539 if entry.relative_path().ends_with(".pack") {
540
541 let mut pack_file_path = data_path.to_path_buf();
542 pack_file_path.push(entry.relative_path());
543 match &language {
544 Some(language) => {
545
546 if entry.relative_path().contains("local_") {
548 let language = "local_".to_owned() + language;
549 if entry.relative_path().contains(&language) {
550 entry.path_from_manifest_entry(pack_file_path)
551 } else {
552 None
553 }
554 } else {
555 entry.path_from_manifest_entry(pack_file_path)
556 }
557 }
558 None => entry.path_from_manifest_entry(pack_file_path),
559 }
560 } else { None }
561 ).collect::<Vec<PathBuf>>();
562
563 paths.sort();
564 Ok(paths)
565 }
566
567 Err(_) => self.ca_packs_paths_no_manifest(game_path, &language)
569 }
570 }
571 }
572
573 fn ca_packs_paths_no_manifest(&self, game_path: &Path, language: &Option<String>) -> Result<Vec<PathBuf>> {
575 let data_path = self.data_path(game_path)?;
576 let install_type = self.install_type(game_path)?;
577 let vanilla_packs = &self.install_data.get(&install_type).ok_or_else(|| RLibError::GameInstallTypeNotSupported(self.display_name.to_string(), install_type.to_string()))?.vanilla_packs;
578 let language_pack = language.clone().map(|lang| format!("local_{lang}"));
579 if !vanilla_packs.is_empty() {
580 Ok(vanilla_packs.iter().filter_map(|pack_name| {
581
582 let mut pack_file_path = data_path.to_path_buf();
583 pack_file_path.push(pack_name);
584 match language_pack {
585 Some(ref language_pack) => {
586
587 if !pack_name.is_empty() && pack_name.starts_with("local_") {
589 if pack_name.starts_with(language_pack) {
590 std::fs::canonicalize(pack_file_path).ok()
591 } else {
592 None
593 }
594 } else {
595 std::fs::canonicalize(pack_file_path).ok()
596 }
597 }
598 None => std::fs::canonicalize(pack_file_path).ok(),
599 }
600 }).collect::<Vec<PathBuf>>())
601 }
602
603 else {
605 Ok(files_from_subdir(&data_path, false)?.iter()
606 .filter_map(|x| if let Some(extension) = x.extension() {
607 if extension.to_string_lossy().to_lowercase() == "pack" {
608 Some(x.to_owned())
609 } else { None }
610 } else { None }).collect::<Vec<PathBuf>>()
611 )
612 }
613 }
614
615 pub fn game_launch_command(&self, game_path: &Path) -> Result<String> {
617 let install_type = self.install_type(game_path)?;
618
619 match install_type {
620 InstallType::LnxSteam |
621 InstallType::WinSteam => {
622 let store_id = self.install_data.get(&install_type).ok_or_else(|| RLibError::GameInstallTypeNotSupported(self.display_name.to_string(), install_type.to_string()))?.store_id();
623 Ok(format!("steam://rungameid/{store_id}"))
624 },
625 _ => Err(RLibError::GameInstallLaunchNotSupported(self.display_name.to_string(), install_type.to_string())),
626 }
627 }
628
629 pub fn executable_path(&self, game_path: &Path) -> Option<PathBuf> {
631 let install_type = self.install_type(game_path).ok()?;
632 let install_data = self.install_data.get(&install_type)?;
633 let executable_path = game_path.join(install_data.executable());
634
635 Some(executable_path)
636 }
637
638 pub fn config_path(&self, game_path: &Path) -> Option<PathBuf> {
640 let install_type = self.install_type(game_path).ok()?;
641 let install_data = self.install_data.get(&install_type)?;
642 let config_folder = install_data.config_folder.as_ref()?;
643
644 ProjectDirs::from("com", "The Creative Assembly", config_folder).map(|dir| {
645 let mut dir = dir.config_dir().to_path_buf();
646 dir.pop();
647 dir
648 })
649 }
650
651 pub fn is_file_banned(&self, path: &str) -> bool {
653 let path = path.to_lowercase();
654 self.banned_packedfiles.iter().any(|x| path.starts_with(x))
655 }
656
657 pub fn tool_var(&self, var: &str) -> Option<&String> {
659 self.tool_vars.get(var)
660 }
661
662 pub fn game_locale_from_file(&self, game_path: &Path) -> Result<Option<String>> {
664 match self.locale_file_name() {
665 Some(locale_file) => {
666 let language_path = self.language_path(game_path)?;
667 let locale_path = language_path.join(locale_file);
668 let mut language = String::new();
669 if let Ok(mut file) = File::open(locale_path) {
670 file.read_to_string(&mut language)?;
671
672 let language = match &*language {
673 "BR" => BRAZILIAN.to_owned(),
674 "CN" => SIMPLIFIED_CHINESE.to_owned(),
675 "CZ" => CZECH.to_owned(),
676 "EN" => ENGLISH.to_owned(),
677 "FR" => FRENCH.to_owned(),
678 "DE" => GERMAN.to_owned(),
679 "IT" => ITALIAN.to_owned(),
680 "KR" => KOREAN.to_owned(),
681 "PO" => POLISH.to_owned(),
682 "RU" => RUSSIAN.to_owned(),
683 "ES" => SPANISH.to_owned(),
684 "TR" => TURKISH.to_owned(),
685 "ZH" => TRADITIONAL_CHINESE.to_owned(),
686
687 _ => ENGLISH.to_owned(),
689 };
690
691 #[cfg(feature = "integration_log")] {
692 info!("Language file found, using {language} language.");
693 }
694
695 Ok(Some(language))
696 } else {
697 #[cfg(feature = "integration_log")] {
698 warn!("Missing or unreadable language file under {}. Using english language.", game_path.to_string_lossy());
699 }
700 Ok(Some(ENGLISH.to_owned()))
701 }
702 }
703 None => Ok(None),
704 }
705 }
706
707 pub fn game_version_number(&self, game_path: &Path) -> Option<u32> {
709 match self.key() {
710 KEY_TROY => {
711 let exe_path = self.executable_path(game_path)?;
712 if exe_path.is_file() {
713 let mut data = vec![];
714 let mut file = BufReader::new(File::open(exe_path).ok()?);
715 file.read_to_end(&mut data).ok()?;
716
717 let version_info = pe_version_info(&data).ok()?;
718 let version_info = version_info.fixed()?;
719 let mut version: u32 = 0;
720
721 let major = version_info.dwFileVersion.Major as u32;
723 let minor = version_info.dwFileVersion.Minor as u32;
724 let patch = version_info.dwFileVersion.Patch as u32;
725 let build = version_info.dwFileVersion.Build as u32;
726
727 version += major << 24;
728 version += minor << 16;
729 version += patch << 8;
730 version += build;
731 Some(version)
732 }
733
734 else {
736 None
737 }
738
739 }
740
741 _ => None,
742 }
743 }
744
745 pub fn find_game_install_location(&self) -> Result<Option<PathBuf>> {
749
750 let install_data = if let Some(install_data) = self.install_data.get(&InstallType::WinSteam) {
752 install_data
753 } else if let Some(install_data) = self.install_data.get(&InstallType::LnxSteam) {
754 install_data
755 } else {
756 return Ok(None);
757 };
758
759 if install_data.store_id() > &0 {
760 if let Ok(steamdir) = SteamDir::locate() {
761 return match steamdir.find_app(*install_data.store_id() as u32) {
762 Ok(Some((app, lib))) => {
763 let app_path = lib.resolve_app_dir(&app);
764 if app_path.is_dir() {
765 Ok(Some(app_path.to_path_buf()))
766 } else {
767 Ok(None)
768 }
769 }
770 _ => Ok(None)
771 }
772 }
773 }
774
775 Ok(None)
776 }
777
778 pub fn find_assembly_kit_install_location(&self) -> Result<Option<PathBuf>> {
782
783 let install_data = if let Some(install_data) = self.install_data.get(&InstallType::WinSteam) {
785 install_data
786 } else if let Some(install_data) = self.install_data.get(&InstallType::LnxSteam) {
787 install_data
788 } else {
789 return Ok(None);
790 };
791
792 if install_data.store_id_ak() > &0 {
793 if let Ok(steamdir) = SteamDir::locate() {
794 return match steamdir.find_app(*install_data.store_id_ak() as u32) {
795 Ok(Some((app, lib))) => {
796 let app_path = lib.resolve_app_dir(&app);
797 if app_path.is_dir() {
798 Ok(Some(app_path.to_path_buf()))
799 } else {
800 Ok(None)
801 }
802 }
803 _ => Ok(None)
804 }
805 }
806 }
807
808 Ok(None)
809 }
810
811 pub fn steam_workshop_tags(&self) -> Result<Vec<String>> {
813 Ok(match self.key() {
814 KEY_PHARAOH_DYNASTIES => vec![
815 String::from("mod"),
816 String::from("graphical"),
817 String::from("campaign"),
818 String::from("ui"),
819 String::from("battle"),
820 String::from("overhaul"),
821 String::from("units"),
822 ],
823 KEY_PHARAOH => vec![
824 String::from("mod"),
825 String::from("graphical"),
826 String::from("campaign"),
827 String::from("ui"),
828 String::from("battle"),
829 String::from("overhaul"),
830 String::from("units"),
831 ],
832 KEY_WARHAMMER_3 => vec![
833 String::from("graphical"),
834 String::from("campaign"),
835 String::from("units"),
836 String::from("battle"),
837 String::from("ui"),
838 String::from("maps"),
839 String::from("overhaul"),
840 String::from("compilation"),
841 String::from("cheat"),
842 ],
843 KEY_TROY => vec![
844 String::from("mod"),
845 String::from("ui"),
846 String::from("graphical"),
847 String::from("units"),
848 String::from("battle"),
849 String::from("campaign"),
850 String::from("overhaul"),
851 String::from("compilation"),
852 ],
853 KEY_THREE_KINGDOMS => vec![
854 String::from("mod"),
855 String::from("graphical"),
856 String::from("overhaul"),
857 String::from("ui"),
858 String::from("battle"),
859 String::from("campaign"),
860 String::from("maps"),
861 String::from("units"),
862 String::from("compilation"),
863 ],
864 KEY_WARHAMMER_2 => vec![
865 String::from("mod"),
866 String::from("Units"),
867 String::from("Battle"),
868 String::from("Graphical"),
869 String::from("UI"),
870 String::from("Campaign"),
871 String::from("Maps"),
872 String::from("Overhaul"),
873 String::from("Compilation"),
874 String::from("Mod Manager"),
875 String::from("Skills"),
876 String::from("map"),
877 ],
878 KEY_WARHAMMER => vec![
879 String::from("mod"),
880 String::from("UI"),
881 String::from("Graphical"),
882 String::from("Overhaul"),
883 String::from("Battle"),
884 String::from("Campaign"),
885 String::from("Compilation"),
886 String::from("Units"),
887 String::from("Maps"),
888 String::from("Spanish"),
889 String::from("English"),
890 String::from("undefined"),
891 String::from("map"),
892 ],
893 KEY_THRONES_OF_BRITANNIA => vec![
894 String::from("mod"),
895 String::from("ui"),
896 String::from("battle"),
897 String::from("campaign"),
898 String::from("units"),
899 String::from("compilation"),
900 String::from("graphical"),
901 String::from("overhaul"),
902 String::from("maps"),
903 ],
904 KEY_ATTILA => vec![
905 String::from("mod"),
906 String::from("UI"),
907 String::from("Graphical"),
908 String::from("Battle"),
909 String::from("Campaign"),
910 String::from("Units"),
911 String::from("Overhaul"),
912 String::from("Compilation"),
913 String::from("Maps"),
914 String::from("version_2"),
915 String::from("Czech"),
916 String::from("Danish"),
917 String::from("English"),
918 String::from("Finnish"),
919 String::from("French"),
920 String::from("German"),
921 String::from("Hungarian"),
922 String::from("Italian"),
923 String::from("Japanese"),
924 String::from("Korean"),
925 String::from("Norwegian"),
926 String::from("Romanian"),
927 String::from("Russian"),
928 String::from("Spanish"),
929 String::from("Swedish"),
930 String::from("Thai"),
931 String::from("Turkish"),
932 ],
933 KEY_ROME_2 => vec![
934 String::from("mod"),
935 String::from("Units"),
936 String::from("Battle"),
937 String::from("Overhaul"),
938 String::from("Compilation"),
939 String::from("Campaign"),
940 String::from("Graphical"),
941 String::from("UI"),
942 String::from("Maps"),
943 String::from("version_2"),
944 String::from("English"),
945 String::from("gribble"),
946 String::from("tribble"),
947 ],
948 KEY_SHOGUN_2 => vec![
949 String::from("map"),
950 String::from("historical"),
951 String::from("multiplayer"),
952 String::from("mod"),
953 String::from("version_2"),
954 String::from("English"),
955 String::from("ui"),
956 String::from("graphical"),
957 String::from("overhaul"),
958 String::from("units"),
959 String::from("campaign"),
960 String::from("battle"),
961 ],
962 _ => return Err(RLibError::GameDoesntSupportWorkshop(self.key().to_owned()))
963 })
964 }
965
966 pub fn game_by_steam_id(steam_id: u64) -> Result<Self> {
968 let games = SupportedGames::default();
969 for game in games.games() {
970
971 match game.install_data.get(&InstallType::WinSteam) {
973 Some(install_data) => if install_data.store_id == steam_id {
974 return Ok(game.clone());
975 } else {
976 continue;
977 }
978 None => continue,
979 }
980 }
981
982 Err(RLibError::SteamIDDoesntBelongToKnownGame(steam_id))
983 }
984}