diff --git a/src/infra/logging/session.rs b/src/infra/logging/session.rs index 7a5b867..e30977c 100644 --- a/src/infra/logging/session.rs +++ b/src/infra/logging/session.rs @@ -67,6 +67,12 @@ pub struct LaunchVerification { pub steam_api_initialized: Option, pub steam_ownership_confirmed: Option, pub steam_client_artifact: Option, // "local", "windows", "host" + pub effective_game_wineprefix: Option, + pub effective_steam_wineprefix: Option, + pub steam_client_install_path_exposed_to_game: Option, + pub steam_client_install_path_source: Option, // "real" vs "fake_trap" + pub per_game_prefix_requested: bool, + pub per_game_prefix_honored: bool, pub log_head: Vec, pub log_tail: Vec, } diff --git a/src/infra/runners/wine_tkg.rs b/src/infra/runners/wine_tkg.rs index 8ae6872..b23ad8b 100644 --- a/src/infra/runners/wine_tkg.rs +++ b/src/infra/runners/wine_tkg.rs @@ -24,14 +24,16 @@ impl Runner for WineTkgRunner { .map(|c| c.steam_prefix_mode.clone()) .unwrap_or(ctx.launcher_config.steam_prefix_mode.clone()); + let user_config_store: crate::models::UserConfigStore = ctx.user_config.as_ref().map(|c| { + let mut store = HashMap::new(); + store.insert(ctx.app.app_id, c.clone()); + store + }).unwrap_or_default().into(); + let effective_game_prefix = crate::utils::steam_wineprefix_for_game( &ctx.launcher_config, ctx.app.app_id, - &ctx.user_config.as_ref().map(|c| { - let mut store = HashMap::new(); - store.insert(ctx.app.app_id, c.clone()); - store - }).unwrap_or_default().into() + &user_config_store ); std::fs::create_dir_all(&effective_game_prefix) .map_err(|e| LaunchError::new(LaunchErrorKind::Permission, format!("failed creating {}", effective_game_prefix.display())).with_source(anyhow!(e)))?; @@ -172,6 +174,10 @@ impl Runner for WineTkgRunner { if !ctx.verification_ptr.is_null() { let v = &mut *ctx.verification_ptr; v.steam_running_before_launch = steam_running; + v.effective_game_wineprefix = Some(effective_game_prefix.to_string_lossy().to_string()); + v.effective_steam_wineprefix = Some(steam_wineprefix.to_string_lossy().to_string()); + v.per_game_prefix_requested = steam_prefix_mode == crate::models::SteamPrefixMode::PerGame; + v.per_game_prefix_honored = effective_game_prefix == steam_wineprefix; } } @@ -354,14 +360,16 @@ impl Runner for WineTkgRunner { .join("compatdata") .join(&app_id_str); + let user_config_store: crate::models::UserConfigStore = ctx.user_config.as_ref().map(|c| { + let mut store = HashMap::new(); + store.insert(ctx.app.app_id, c.clone()); + store + }).unwrap_or_default().into(); + let effective_game_prefix = crate::utils::steam_wineprefix_for_game( &ctx.launcher_config, ctx.app.app_id, - &ctx.user_config.as_ref().map(|c| { - let mut store = HashMap::new(); - store.insert(ctx.app.app_id, c.clone()); - store - }).unwrap_or_default().into() + &user_config_store ); env.insert("SteamAppId".to_string(), app_id_str.clone()); @@ -421,8 +429,9 @@ impl Runner for WineTkgRunner { .unwrap_or("wine") }; + let active_runner_path = crate::utils::resolve_runner(proton, &library_root); let _components = crate::utils::detect_runner_components( - &crate::utils::resolve_runner(proton, &library_root), + &active_runner_path, Some(&effective_game_prefix), ); @@ -609,10 +618,60 @@ impl Runner for WineTkgRunner { } env.insert("WINEPATH".to_string(), wine_path.join(";")); - let config_dir = crate::config::config_dir().map_err(|e| LaunchError::new(LaunchErrorKind::Environment, "failed to get config dir").with_source(e))?; - let fake_env = crate::utils::setup_fake_steam_trap(&config_dir) - .map_err(|e| LaunchError::new(LaunchErrorKind::Permission, "failed to setup fake steam trap").with_source(e))?; - env.insert("STEAM_COMPAT_CLIENT_INSTALL_PATH".to_string(), fake_env.to_string_lossy().to_string()); + let use_steam_runtime = ctx.user_config.as_ref() + .map(|c| c.use_steam_runtime) + .unwrap_or(ctx.app.app_id == 209000); // Default to true for Batman + + if use_steam_runtime { + let steam_cfg = crate::utils::get_master_steam_config(); + let steam_prefix_mode = ctx.user_config.as_ref() + .map(|c| c.steam_prefix_mode.clone()) + .unwrap_or(ctx.launcher_config.steam_prefix_mode.clone()); + + let steam_client_path = match steam_prefix_mode { + crate::models::SteamPrefixMode::Shared => { + steam_cfg.steam_exe.as_ref().and_then(|e| e.parent().map(|p| p.to_path_buf())) + } + crate::models::SteamPrefixMode::PerGame => { + Some(effective_game_prefix.join("drive_c/Program Files (x86)/Steam")) + } + }; + + if let Some(path) = steam_client_path { + env.insert("STEAM_COMPAT_CLIENT_INSTALL_PATH".to_string(), path.to_string_lossy().to_string()); + unsafe { + if !ctx.verification_ptr.is_null() { + let v = &mut *ctx.verification_ptr; + v.steam_client_install_path_exposed_to_game = Some(path.to_string_lossy().to_string()); + v.steam_client_install_path_source = Some("real".to_string()); + } + } + } else { + let config_dir = crate::config::config_dir().map_err(|e| LaunchError::new(LaunchErrorKind::Environment, "failed to get config dir").with_source(e))?; + let fake_env = crate::utils::setup_fake_steam_trap(&config_dir) + .map_err(|e| LaunchError::new(LaunchErrorKind::Permission, "failed to setup fake steam trap").with_source(e))?; + env.insert("STEAM_COMPAT_CLIENT_INSTALL_PATH".to_string(), fake_env.to_string_lossy().to_string()); + unsafe { + if !ctx.verification_ptr.is_null() { + let v = &mut *ctx.verification_ptr; + v.steam_client_install_path_exposed_to_game = Some(fake_env.to_string_lossy().to_string()); + v.steam_client_install_path_source = Some("fake_trap".to_string()); + } + } + } + } else { + let config_dir = crate::config::config_dir().map_err(|e| LaunchError::new(LaunchErrorKind::Environment, "failed to get config dir").with_source(e))?; + let fake_env = crate::utils::setup_fake_steam_trap(&config_dir) + .map_err(|e| LaunchError::new(LaunchErrorKind::Permission, "failed to setup fake steam trap").with_source(e))?; + env.insert("STEAM_COMPAT_CLIENT_INSTALL_PATH".to_string(), fake_env.to_string_lossy().to_string()); + unsafe { + if !ctx.verification_ptr.is_null() { + let v = &mut *ctx.verification_ptr; + v.steam_client_install_path_exposed_to_game = Some(fake_env.to_string_lossy().to_string()); + v.steam_client_install_path_source = Some("fake_trap".to_string()); + } + } + } if let Ok(display) = std::env::var("DISPLAY") { env.insert("DISPLAY".to_string(), display); diff --git a/src/launch/pipeline.rs b/src/launch/pipeline.rs index 023966d..8f098e5 100644 --- a/src/launch/pipeline.rs +++ b/src/launch/pipeline.rs @@ -1315,6 +1315,21 @@ impl LaunchPipeline { if let Some(ref art) = ctx.verification.steam_client_artifact { metadata.insert("steam_client_artifact".to_string(), art.clone()); } + if let Some(ref pfx) = ctx.verification.effective_game_wineprefix { + metadata.insert("effective_game_wineprefix".to_string(), pfx.clone()); + } + if let Some(ref pfx) = ctx.verification.effective_steam_wineprefix { + metadata.insert("effective_steam_wineprefix".to_string(), pfx.clone()); + } + if let Some(ref path) = ctx.verification.steam_client_install_path_exposed_to_game { + metadata.insert("steam_client_install_path_exposed_to_game".to_string(), path.clone()); + } + if let Some(ref source) = ctx.verification.steam_client_install_path_source { + metadata.insert("steam_client_install_path_source".to_string(), source.clone()); + } + metadata.insert("per_game_prefix_requested".to_string(), ctx.verification.per_game_prefix_requested.to_string()); + metadata.insert("per_game_prefix_honored".to_string(), ctx.verification.per_game_prefix_honored.to_string()); + metadata.insert("steam_running_before_launch".to_string(), ctx.verification.steam_running_before_launch.to_string()); metadata.insert("steam_auto_start_attempted".to_string(), ctx.verification.steam_auto_start_attempted.to_string()); metadata.insert("steam_auto_start_failed".to_string(), ctx.verification.steam_auto_start_failed.to_string()); diff --git a/src/utils.rs b/src/utils.rs index 8bb4e25..b644f87 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -1162,11 +1162,16 @@ pub fn cleanup_dll_symlinks(prefix: &Path) -> Result<()> { pub fn steam_wineprefix_for_game( config: &crate::config::LauncherConfig, app_id: u32, - _user_configs: &crate::models::UserConfigStore, + user_configs: &crate::models::UserConfigStore, ) -> std::path::PathBuf { - if config.use_shared_compat_data { + let use_per_game_compat_data = user_configs.get(&app_id) + .map(|c| c.use_steam_runtime && c.steam_prefix_mode == crate::models::SteamPrefixMode::PerGame) + .unwrap_or(config.use_shared_compat_data); + + if use_per_game_compat_data { std::path::PathBuf::from(&config.steam_library_path) - .join("steamapps/compatdata") + .join("steamapps") + .join("compatdata") .join(app_id.to_string()) .join("pfx") } else {