modde_games/generic/
mod.rs1pub(crate) mod leak;
5pub mod loader;
6pub mod manage;
7pub mod spec;
8
9use std::path::{Path, PathBuf};
10
11use smallvec::SmallVec;
12
13use crate::traits::GamePlugin;
14
15use self::spec::GameSpec;
16
17pub struct GenericGame {
19 id: String,
20 name: String,
21 install_path_override: Option<PathBuf>,
22 install_dir_name: Option<String>,
23 mod_dir: Option<PathBuf>,
24 executable_dir: PathBuf,
25 proxy_dlls: Vec<String>,
26 steam_app_id: Option<String>,
27 nexus_domain: Option<String>,
28}
29
30impl GenericGame {
31 pub fn new(
33 id: impl Into<String>,
34 name: impl Into<String>,
35 install_path_override: Option<PathBuf>,
36 mod_dir: impl Into<PathBuf>,
37 ) -> Self {
38 Self {
39 id: id.into(),
40 name: name.into(),
41 install_path_override,
42 install_dir_name: None,
43 mod_dir: Some(mod_dir.into()),
44 executable_dir: PathBuf::new(),
45 proxy_dlls: Vec::new(),
46 steam_app_id: None,
47 nexus_domain: None,
48 }
49 }
50
51 pub fn from_spec(spec: GameSpec) -> Self {
53 Self {
54 id: spec.id,
55 name: spec.display_name,
56 install_path_override: spec.install_path_override,
57 install_dir_name: spec.install_dir_name,
58 mod_dir: spec.mod_dir,
59 executable_dir: spec.executable_dir,
60 proxy_dlls: spec.proxy_dlls,
61 steam_app_id: spec.steam_app_id,
62 nexus_domain: spec.nexus_domain,
63 }
64 }
65
66 fn install_from_steam_dir_name(dir_name: &str) -> Option<PathBuf> {
67 modde_core::paths::steam_library_folders()
68 .into_iter()
69 .map(|library| library.join("steamapps/common").join(dir_name))
70 .find(|path| path.is_dir())
71 }
72}
73
74impl GamePlugin for GenericGame {
75 fn game_id(&self) -> &str {
76 &self.id
77 }
78
79 fn display_name(&self) -> &str {
80 &self.name
81 }
82
83 fn detect_install(&self) -> Option<PathBuf> {
84 if let Some(path) = self
85 .install_path_override
86 .as_ref()
87 .filter(|path| path.is_dir())
88 {
89 return Some(path.clone());
90 }
91
92 if let Some(dir_name) = self.install_dir_name.as_deref()
93 && let Some(path) = Self::install_from_steam_dir_name(dir_name)
94 {
95 return Some(path);
96 }
97
98 crate::detection::find_game_install(&modde_core::GameId::from(self.game_id()))
99 }
100
101 fn mod_directory(&self, install: &Path) -> PathBuf {
102 self.mod_dir
103 .as_ref()
104 .map_or_else(|| install.to_path_buf(), |dir| install.join(dir))
105 }
106
107 fn executable_dir(&self, install: &Path) -> PathBuf {
108 install.join(&self.executable_dir)
109 }
110
111 fn wine_dll_overrides(&self, install: &Path) -> SmallVec<[String; 4]> {
112 let executable_dir = self.executable_dir(install);
113 self.proxy_dlls
114 .iter()
115 .filter(|name| executable_dir.join(format!("{name}.dll")).exists())
116 .cloned()
117 .collect()
118 }
119
120 fn steam_app_id_u32(&self) -> Option<u32> {
121 self.steam_app_id.as_deref()?.parse().ok()
122 }
123
124 fn nexus_game_domain(&self) -> Option<&str> {
125 self.nexus_domain.as_deref()
126 }
127}