@@ -18,15 +18,21 @@ impl Runner for WineTkgRunner {
1818 . map ( |c| c. steam_prefix_mode . clone ( ) )
1919 . unwrap_or ( ctx. launcher_config . steam_prefix_mode . clone ( ) ) ;
2020
21- let compat_data_path = library_root
22- . join ( "steamapps" )
23- . join ( "compatdata" )
24- . join ( ctx. app . app_id . to_string ( ) ) ;
25- let target_prefix_path = compat_data_path. join ( "pfx" ) ;
26- std:: fs:: create_dir_all ( & target_prefix_path)
27- . map_err ( |e| LaunchError :: new ( LaunchErrorKind :: Permission , format ! ( "failed creating {}" , target_prefix_path. display( ) ) ) . with_source ( anyhow ! ( e) ) ) ?;
21+ let effective_game_prefix = crate :: utils:: steam_wineprefix_for_game (
22+ & ctx. launcher_config ,
23+ ctx. app . app_id ,
24+ & ctx. user_config . as_ref ( ) . map ( |c| {
25+ let mut store = HashMap :: new ( ) ;
26+ store. insert ( ctx. app . app_id , c. clone ( ) ) ;
27+ store
28+ } ) . unwrap_or_default ( ) . into ( )
29+ ) ;
30+ std:: fs:: create_dir_all ( & effective_game_prefix)
31+ . map_err ( |e| LaunchError :: new ( LaunchErrorKind :: Permission , format ! ( "failed creating {}" , effective_game_prefix. display( ) ) ) . with_source ( anyhow ! ( e) ) ) ?;
2832
29- let mut steam_wineprefix = target_prefix_path. clone ( ) ;
33+ tracing:: info!( "Effective game prefix: {}" , effective_game_prefix. display( ) ) ;
34+ tracing:: info!( "Shared steam compatibility data enabled: {}" , ctx. launcher_config. use_shared_compat_data) ;
35+ tracing:: info!( "Steam Runtime Prefix Mode: {:?}" , steam_prefix_mode) ;
3036
3137 if use_steam_runtime {
3238 let base_config = crate :: config:: config_dir ( ) . map_err ( |e| LaunchError :: new ( LaunchErrorKind :: Environment , "failed to get config dir" ) . with_source ( e) ) ?;
@@ -46,51 +52,74 @@ impl Runner for WineTkgRunner {
4652 ) . with_context ( "master_prefix" , master_prefix. to_string_lossy ( ) ) ) ;
4753 }
4854 Some ( master_steam_dir) => {
49- let master_wineprefix_original = crate :: utils:: resolve_master_wineprefix ( ) ;
50-
51- let prefix_steam_dir = match steam_prefix_mode {
55+ let ( prefix_steam_dir, steam_wineprefix) = match steam_prefix_mode {
5256 crate :: models:: SteamPrefixMode :: Shared => {
53- steam_wineprefix = master_wineprefix_original. clone ( ) ;
54- master_steam_dir. clone ( )
57+ ( master_steam_dir. clone ( ) , crate :: utils:: resolve_master_wineprefix ( ) )
5558 }
5659 crate :: models:: SteamPrefixMode :: PerGame => {
57- let target_steam_dir = target_prefix_path
60+ let target_steam_dir = effective_game_prefix
5861 . join ( "drive_c/Program Files (x86)/Steam" ) ;
5962
6063 tracing:: info!(
61- "Linking/Cloning {} → {}" ,
62- master_steam_dir. display( ) ,
64+ "Deploying required Steam runtime files to {}" ,
6365 target_steam_dir. display( )
6466 ) ;
65- let _ = std:: fs:: create_dir_all ( target_steam_dir. parent ( ) . unwrap ( ) ) ;
66- #[ cfg( unix) ]
67- {
68- if !target_steam_dir. exists ( ) {
69- if let Err ( e) =
70- std:: os:: unix:: fs:: symlink ( & master_steam_dir, & target_steam_dir)
67+ let _ = std:: fs:: create_dir_all ( & target_steam_dir) ;
68+
69+ let required_files = [
70+ "steam.exe" ,
71+ "steamclient.dll" ,
72+ "steamclient64.dll" ,
73+ "tier0_s.dll" ,
74+ "tier0_s64.dll" ,
75+ "vstdlib_s.dll" ,
76+ "vstdlib_s64.dll" ,
77+ ] ;
78+
79+ for file in required_files {
80+ let src = master_steam_dir. join ( file) ;
81+ let dst = target_steam_dir. join ( file) ;
82+ if src. exists ( ) && !dst. exists ( ) {
83+ #[ cfg( unix) ]
84+ {
85+ if let Err ( e) = std:: os:: unix:: fs:: symlink ( & src, & dst) {
86+ tracing:: warn!( "Symlink failed for {}, falling back to copy: {}" , file, e) ;
87+ let _ = std:: fs:: copy ( & src, & dst) ;
88+ }
89+ }
90+ #[ cfg( not( unix) ) ]
7191 {
72- tracing:: warn!( "Symlink failed, falling back to copy: {}" , e) ;
73- let _ = crate :: utils:: copy_dir_all (
74- & master_steam_dir,
75- & target_steam_dir,
76- ) ;
92+ let _ = std:: fs:: copy ( & src, & dst) ;
7793 }
7894 }
7995 }
80- #[ cfg( not( unix) ) ]
81- {
82- let _ = crate :: utils:: copy_dir_all (
83- & master_steam_dir,
84- & target_steam_dir,
85- ) ;
96+
97+ // Also symlink required subdirectories
98+ let required_dirs = [ "bin" , "public" ] ;
99+ for dir in required_dirs {
100+ let src = master_steam_dir. join ( dir) ;
101+ let dst = target_steam_dir. join ( dir) ;
102+ if src. exists ( ) && !dst. exists ( ) {
103+ #[ cfg( unix) ]
104+ {
105+ if let Err ( e) = std:: os:: unix:: fs:: symlink ( & src, & dst) {
106+ tracing:: warn!( "Symlink failed for {}, falling back to copy: {}" , dir, e) ;
107+ let _ = crate :: utils:: copy_dir_all ( & src, & dst) ;
108+ }
109+ }
110+ #[ cfg( not( unix) ) ]
111+ {
112+ let _ = crate :: utils:: copy_dir_all ( & src, & dst) ;
113+ }
114+ }
86115 }
87- target_steam_dir
116+
117+ ( target_steam_dir, effective_game_prefix. clone ( ) )
88118 }
89119 } ;
90120
91- println ! ( "--- STEAM LAUNCH DEBUG ---" ) ;
92- println ! ( "Prefix Steam dir : {}" , prefix_steam_dir. display( ) ) ;
93- println ! ( "Steam WINEPREFIX : {}" , steam_wineprefix. display( ) ) ;
121+ tracing:: debug!( "Runtime Steam dir : {}" , prefix_steam_dir. display( ) ) ;
122+ tracing:: debug!( "Runtime WINEPREFIX : {}" , steam_wineprefix. display( ) ) ;
94123
95124 SteamClient :: write_headless_steam_cfg ( & prefix_steam_dir) ;
96125
@@ -254,25 +283,24 @@ impl Runner for WineTkgRunner {
254283 let app_id_str = ctx. app . app_id . to_string ( ) ;
255284
256285 let library_root = PathBuf :: from ( & ctx. launcher_config . steam_library_path ) ;
257- let steam_prefix_mode = ctx. user_config . as_ref ( )
258- . map ( |c| c. steam_prefix_mode . clone ( ) )
259- . unwrap_or ( ctx. launcher_config . steam_prefix_mode . clone ( ) ) ;
260- let use_steam_runtime = ctx. user_config . as_ref ( ) . map ( |c| c. use_steam_runtime ) . unwrap_or ( false ) ;
261-
262286 let compat_data_path = library_root
263287 . join ( "steamapps" )
264288 . join ( "compatdata" )
265289 . join ( & app_id_str) ;
266- let target_prefix_path = compat_data_path. join ( "pfx" ) ;
267290
268- let mut game_wineprefix = target_prefix_path. clone ( ) ;
269- if use_steam_runtime && matches ! ( steam_prefix_mode, crate :: models:: SteamPrefixMode :: Shared ) {
270- game_wineprefix = crate :: utils:: resolve_master_wineprefix ( ) ;
271- }
291+ let effective_game_prefix = crate :: utils:: steam_wineprefix_for_game (
292+ & ctx. launcher_config ,
293+ ctx. app . app_id ,
294+ & ctx. user_config . as_ref ( ) . map ( |c| {
295+ let mut store = HashMap :: new ( ) ;
296+ store. insert ( ctx. app . app_id , c. clone ( ) ) ;
297+ store
298+ } ) . unwrap_or_default ( ) . into ( )
299+ ) ;
272300
273301 env. insert ( "SteamAppId" . to_string ( ) , app_id_str. clone ( ) ) ;
274302 env. insert ( "SteamGameId" . to_string ( ) , app_id_str) ;
275- env. insert ( "WINEPREFIX" . to_string ( ) , game_wineprefix . to_string_lossy ( ) . to_string ( ) ) ;
303+ env. insert ( "WINEPREFIX" . to_string ( ) , effective_game_prefix . to_string_lossy ( ) . to_string ( ) ) ;
276304 env. insert ( "STEAM_COMPAT_DATA_PATH" . to_string ( ) , compat_data_path. to_string_lossy ( ) . to_string ( ) ) ;
277305
278306 let glc = ctx. user_config . as_ref ( )
@@ -310,7 +338,7 @@ impl Runner for WineTkgRunner {
310338
311339 let _components = crate :: utils:: detect_runner_components (
312340 & crate :: utils:: resolve_runner ( proton, & library_root) ,
313- Some ( & game_wineprefix ) ,
341+ Some ( & effective_game_prefix ) ,
314342 ) ;
315343
316344 // 1. Resolve DX8-11 policy (GraphicsBackendPolicy) - CONSERVATIVE
@@ -339,6 +367,13 @@ impl Runner for WineTkgRunner {
339367 let effective_vkd3d_proton = glc. vkd3d_proton_enabled || policy_vkd3dp;
340368 let effective_vkd3d = glc. vkd3d_enabled || policy_vkd3dw;
341369
370+ // NVAPI Support
371+ let nvapi_active = _components. nvapi . is_some ( ) ;
372+ if nvapi_active {
373+ tracing:: info!( "NVAPI component detected, will be exposed to game" ) ;
374+ }
375+
376+ let use_symlinks = glc. use_symlinks_in_prefix ;
342377 let mut dll_overrides = crate :: utils:: build_dll_overrides (
343378 effective_dxvk,
344379 effective_vkd3d_proton,
@@ -351,8 +386,10 @@ impl Runner for WineTkgRunner {
351386
352387 // Enhance overrides with resolved DLL providers
353388 for res in & ctx. dll_resolutions {
354- if let crate :: launch:: dll_provider_resolver:: DllProvider :: GameLocal = res. chosen_provider {
355- // Ensure native wins for game-local DLLs
389+ if res. chosen_provider == crate :: launch:: dll_provider_resolver:: DllProvider :: GameLocal ||
390+ ( res. chosen_provider == crate :: launch:: dll_provider_resolver:: DllProvider :: Custom && !use_symlinks) ||
391+ ( res. chosen_provider == crate :: launch:: dll_provider_resolver:: DllProvider :: Runner && res. name . contains ( "nvapi" ) ) {
392+ // Ensure native wins for game-local or non-symlinked custom DLLs
356393 if !dll_overrides. contains ( & format ! ( "{}=n" , res. name) ) {
357394 dll_overrides. push_str ( & format ! ( ";{}=n" , res. name) ) ;
358395 }
@@ -370,15 +407,19 @@ impl Runner for WineTkgRunner {
370407 // WITHOUT THIS, d3d12=n,b finds whatever is in the prefix's system32 instead.
371408 // CONSERVATIVE: only include paths for DLLs that are actually requested to be native.
372409 let mut wine_dll_dirs: Vec < String > = Vec :: new ( ) ;
410+ let use_symlinks = glc. use_symlinks_in_prefix ;
373411
374412 for res in & ctx. dll_resolutions {
375- if let crate :: launch:: dll_provider_resolver:: DllProvider :: Runner = res. chosen_provider {
413+ if ( res. chosen_provider == crate :: launch:: dll_provider_resolver:: DllProvider :: Runner ||
414+ res. chosen_provider == crate :: launch:: dll_provider_resolver:: DllProvider :: Custom ) && !use_symlinks
415+ {
376416 // Check if this DLL is actually selected for use by the current policy/overrides
377417 let name = res. name . to_lowercase ( ) ;
378418 let is_dxvk_dll = matches ! ( name. as_str( ) , "d3d8" | "d3d9" | "d3d10" | "d3d10_1" | "d3d10core" | "d3d11" | "dxgi" ) ;
379419 let is_d3d12_dll = matches ! ( name. as_str( ) , "d3d12" | "d3d12core" | "libvkd3d-1" | "libvkd3d-shader-1" ) ;
380420
381- let selected = ( is_dxvk_dll && effective_dxvk) || ( is_d3d12_dll && ( effective_vkd3d_proton || effective_vkd3d) ) ;
421+ let is_nvapi_dll = matches ! ( name. as_str( ) , "nvapi" | "nvapi64" | "nvofapi64" ) ;
422+ let selected = ( is_dxvk_dll && effective_dxvk) || ( is_d3d12_dll && ( effective_vkd3d_proton || effective_vkd3d) ) || is_nvapi_dll;
382423
383424 if !selected {
384425 continue ;
0 commit comments