modde_games/tools/
reshade.rs1use std::path::{Path, PathBuf};
8
9use anyhow::{Context, Result};
10use smallvec::{SmallVec, smallvec};
11use tracing::info;
12
13use super::{
14 AppliedFiles, GameTool, ToolAvailability, ToolCategory, ToolConfig,
15};
16
17pub static RESHADE: ReShade = ReShade;
18
19pub struct ReShade;
20
21impl GameTool for ReShade {
22 fn tool_id(&self) -> &'static str {
23 "reshade"
24 }
25
26 fn display_name(&self) -> &'static str {
27 "ReShade"
28 }
29
30 fn category(&self) -> ToolCategory {
31 ToolCategory::PostProcess
32 }
33
34 fn detect_available(&self) -> ToolAvailability {
35 ToolAvailability::Available {
38 version: Some("user-provided".into()),
39 }
40 }
41
42 fn env_vars(&self, _config: &ToolConfig) -> SmallVec<[(String, String); 4]> {
43 SmallVec::new()
44 }
45
46 fn wine_dll_overrides(&self, config: &ToolConfig) -> SmallVec<[String; 4]> {
47 let dll_name = config.get_str("dll_name").unwrap_or("dxgi");
48 smallvec![dll_name.to_string()]
49 }
50
51 fn apply(&self, game_dir: &Path, config: &ToolConfig) -> Result<AppliedFiles> {
52 let source_dir = config
53 .get_str("source_dir")
54 .map(PathBuf::from)
55 .context("reshade: 'source_dir' setting is required (path to ReShade DLLs)")?;
56
57 let dll_name = config.get_str("dll_name").unwrap_or("dxgi.dll");
58 let exe_subdir = config.get_str("exe_subdir").unwrap_or("");
59
60 let target_dir = if exe_subdir.is_empty() {
61 game_dir.to_path_buf()
62 } else {
63 game_dir.join(exe_subdir)
64 };
65
66 std::fs::create_dir_all(&target_dir)?;
67
68 let mut applied = AppliedFiles::default();
69
70 let src_dll = source_dir.join(dll_name);
72 if src_dll.exists() {
73 let dest = target_dir.join(dll_name);
74 std::fs::copy(&src_dll, &dest)
75 .with_context(|| format!("failed to copy ReShade DLL to {}", dest.display()))?;
76 let rel = dest.strip_prefix(game_dir).unwrap_or(&dest).to_path_buf();
77 applied.files.push(rel);
78 info!(dll = %dll_name, "applied ReShade DLL");
79 }
80
81 let ini = source_dir.join("ReShade.ini");
83 if ini.exists() {
84 let dest = target_dir.join("ReShade.ini");
85 std::fs::copy(&ini, &dest)?;
86 let rel = dest.strip_prefix(game_dir).unwrap_or(&dest).to_path_buf();
87 applied.files.push(rel);
88 }
89
90 for subdir in &["reshade-shaders", "reshade-presets"] {
92 let src = source_dir.join(subdir);
93 if src.is_dir() {
94 let dest = target_dir.join(subdir);
95 copy_dir_recursive(&src, &dest, game_dir, &mut applied)?;
96 }
97 }
98
99 Ok(applied)
100 }
101
102 fn default_config(&self) -> ToolConfig {
103 let mut config = ToolConfig::new("reshade");
104 config.set("dll_name", serde_json::json!("dxgi.dll"));
105 config.set("exe_subdir", serde_json::json!(""));
106 config
107 }
108}
109
110fn copy_dir_recursive(
112 src: &Path,
113 dest: &Path,
114 game_dir: &Path,
115 applied: &mut AppliedFiles,
116) -> Result<()> {
117 std::fs::create_dir_all(dest)?;
118
119 for entry in std::fs::read_dir(src)?.flatten() {
120 let ty = entry.file_type()?;
121 let src_path = entry.path();
122 let dest_path = dest.join(entry.file_name());
123
124 if ty.is_dir() {
125 copy_dir_recursive(&src_path, &dest_path, game_dir, applied)?;
126 } else {
127 std::fs::copy(&src_path, &dest_path)?;
128 let rel = dest_path
129 .strip_prefix(game_dir)
130 .unwrap_or(&dest_path)
131 .to_path_buf();
132 applied.files.push(rel);
133 }
134 }
135
136 Ok(())
137}