aboutsummaryrefslogtreecommitdiff
path: root/libs/nak/src/deps/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'libs/nak/src/deps/mod.rs')
-rw-r--r--libs/nak/src/deps/mod.rs177
1 files changed, 177 insertions, 0 deletions
diff --git a/libs/nak/src/deps/mod.rs b/libs/nak/src/deps/mod.rs
new file mode 100644
index 0000000..a055eb7
--- /dev/null
+++ b/libs/nak/src/deps/mod.rs
@@ -0,0 +1,177 @@
+//! Dependency management via winetricks
+//!
+//! Uses winetricks for all Windows dependency installation.
+
+pub mod tools;
+
+use std::error::Error;
+use std::path::Path;
+use std::sync::atomic::{AtomicBool, Ordering};
+use std::sync::Arc;
+
+use crate::config::AppConfig;
+use crate::logging::{log_error, log_install};
+use crate::runtime_wrap;
+use crate::steam::SteamProton;
+
+// Re-export tools
+pub use tools::{check_command_available, ensure_cabextract, ensure_winetricks, get_winetricks_path};
+
+/// Standard winetricks verbs for MO2 prefix
+pub const STANDARD_VERBS: &[&str] = &[
+ "vcrun2022", // Visual C++ 2015-2022 Runtime
+ "dotnet6", // .NET 6.0
+ "dotnet7", // .NET 7.0
+ "dotnet8", // .NET 8.0
+ "dotnetdesktop6", // .NET Desktop Runtime 6.0
+ "d3dcompiler_47", // DirectX Compiler 47
+ "d3dcompiler_43", // DirectX Compiler 43
+ "d3dx9", // DirectX 9 (all versions)
+ "d3dx11_43", // DirectX 11
+ "xact", // XACT Audio (32-bit)
+ "xact_x64", // XACT Audio (64-bit)
+];
+
+
+/// Run winetricks to install dependencies
+pub fn run_winetricks(
+ prefix_path: &Path,
+ proton: &SteamProton,
+ verbs: &[&str],
+ log_callback: impl Fn(String),
+) -> Result<(), Box<dyn Error>> {
+ if verbs.is_empty() {
+ return Ok(());
+ }
+
+ let winetricks_path = ensure_winetricks()?;
+ ensure_cabextract()?;
+
+ let Some(wine_bin) = proton.wine_binary() else {
+ return Err("Wine binary not found in Proton".into());
+ };
+
+ let Some(wineserver_bin) = proton.wineserver_binary() else {
+ return Err("Wineserver binary not found in Proton".into());
+ };
+
+ let cache_dir = AppConfig::get_default_cache_dir();
+ std::fs::create_dir_all(&cache_dir)?;
+
+ let verbs_str = verbs.join(" ");
+ log_callback(format!("Installing dependencies via winetricks: {}", verbs_str));
+ log_install(&format!("Running winetricks with verbs: {}", verbs_str));
+
+ let nak_bin = tools::get_nak_bin_path();
+ let current_path = std::env::var("PATH").unwrap_or_default();
+ let new_path = format!("{}:{}", nak_bin.display(), current_path);
+
+ let envs: Vec<(&str, String)> = vec![
+ ("PATH", new_path),
+ ("WINE", wine_bin.display().to_string()),
+ ("WINESERVER", wineserver_bin.display().to_string()),
+ ("WINEPREFIX", prefix_path.display().to_string()),
+ ("WINETRICKS_CACHE", cache_dir.display().to_string()),
+ ];
+ let status = runtime_wrap::build_command(&winetricks_path, &envs)
+ .arg("-q")
+ .args(verbs)
+ .status()?;
+
+ if !status.success() {
+ let err_msg = format!("Winetricks failed with exit code: {:?}", status.code());
+ log_error(&err_msg);
+ return Err(err_msg.into());
+ }
+
+ log_install("Winetricks completed successfully");
+ Ok(())
+}
+
+/// Install all standard dependencies to a prefix
+pub fn install_standard_deps(
+ prefix_path: &Path,
+ proton: &SteamProton,
+ log_callback: impl Fn(String),
+) -> Result<(), Box<dyn Error>> {
+ run_winetricks(prefix_path, proton, STANDARD_VERBS, log_callback)
+}
+
+/// Run winetricks with cancellation support.
+pub fn run_winetricks_cancellable(
+ prefix_path: &Path,
+ proton: &SteamProton,
+ verbs: &[&str],
+ log_callback: impl Fn(String),
+ cancel_flag: &Arc<AtomicBool>,
+) -> Result<(), Box<dyn Error>> {
+ if verbs.is_empty() {
+ return Ok(());
+ }
+
+ let winetricks_path = ensure_winetricks()?;
+ ensure_cabextract()?;
+
+ let Some(wine_bin) = proton.wine_binary() else {
+ return Err("Wine binary not found in Proton".into());
+ };
+
+ let Some(wineserver_bin) = proton.wineserver_binary() else {
+ return Err("Wineserver binary not found in Proton".into());
+ };
+
+ let cache_dir = AppConfig::get_default_cache_dir();
+ std::fs::create_dir_all(&cache_dir)?;
+
+ let verbs_str = verbs.join(" ");
+ log_callback(format!("Installing dependencies via winetricks: {}", verbs_str));
+ log_install(&format!("Running winetricks with verbs: {}", verbs_str));
+
+ let nak_bin = tools::get_nak_bin_path();
+ let current_path = std::env::var("PATH").unwrap_or_default();
+ let new_path = format!("{}:{}", nak_bin.display(), current_path);
+
+ let envs: Vec<(&str, String)> = vec![
+ ("PATH", new_path),
+ ("WINE", wine_bin.display().to_string()),
+ ("WINESERVER", wineserver_bin.display().to_string()),
+ ("WINEPREFIX", prefix_path.display().to_string()),
+ ("WINETRICKS_CACHE", cache_dir.display().to_string()),
+ ];
+ let mut child = runtime_wrap::build_command(&winetricks_path, &envs)
+ .arg("-q")
+ .args(verbs)
+ .spawn()?;
+
+ loop {
+ match child.try_wait()? {
+ Some(status) => {
+ if !status.success() {
+ let err_msg = format!("Winetricks failed with exit code: {:?}", status.code());
+ log_error(&err_msg);
+ return Err(err_msg.into());
+ }
+ log_install("Winetricks completed successfully");
+ return Ok(());
+ }
+ None => {
+ if cancel_flag.load(Ordering::Relaxed) {
+ let _ = child.kill();
+ let _ = child.wait();
+ return Err("Cancelled".into());
+ }
+ std::thread::sleep(std::time::Duration::from_millis(250));
+ }
+ }
+ }
+}
+
+/// Install standard deps with cancellation support
+pub fn install_standard_deps_cancellable(
+ prefix_path: &Path,
+ proton: &SteamProton,
+ log_callback: impl Fn(String),
+ cancel_flag: &Arc<AtomicBool>,
+) -> Result<(), Box<dyn Error>> {
+ run_winetricks_cancellable(prefix_path, proton, STANDARD_VERBS, log_callback, cancel_flag)
+}