diff options
Diffstat (limited to 'libs/nak/src/deps/mod.rs')
| -rw-r--r-- | libs/nak/src/deps/mod.rs | 177 |
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) +} |
