Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 3d3790a83a |
+1
-1
@@ -2177,7 +2177,7 @@ fn group_thousands(digits: &str) -> String {
|
|||||||
let len = bytes.len();
|
let len = bytes.len();
|
||||||
let mut out = String::with_capacity(len + len / 3);
|
let mut out = String::with_capacity(len + len / 3);
|
||||||
for (i, b) in bytes.iter().enumerate() {
|
for (i, b) in bytes.iter().enumerate() {
|
||||||
if i > 0 && (len - i) % 3 == 0 {
|
if i > 0 && (len - i).is_multiple_of(3) {
|
||||||
out.push(',');
|
out.push(',');
|
||||||
}
|
}
|
||||||
out.push(*b as char);
|
out.push(*b as char);
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ pub fn launch(
|
|||||||
}
|
}
|
||||||
|
|
||||||
prepare_prefix(profile, log)?;
|
prepare_prefix(profile, log)?;
|
||||||
|
ensure_dll_override(profile, log);
|
||||||
ensure_license(profile, log)?;
|
ensure_license(profile, log)?;
|
||||||
|
|
||||||
let mut cmd = Command::new(&profile.runner);
|
let mut cmd = Command::new(&profile.runner);
|
||||||
@@ -95,6 +96,99 @@ pub fn launch(
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// The registry key Wine reads DLL overrides from, and the one value the hook needs.
|
||||||
|
///
|
||||||
|
/// Wine loads its own builtin `version.dll` unless an override says otherwise, so the
|
||||||
|
/// game-directory proxy is ignored by default. `WINEDLLOVERRIDES` fixes that only for
|
||||||
|
/// a process we spawn ourselves — it cannot help a player who presses Play in Steam,
|
||||||
|
/// which is why the old advice was to paste launch options by hand (see
|
||||||
|
/// `setup::STEAM_LAUNCH_OPTIONS`). Asking a player to edit launch options is exactly
|
||||||
|
/// the kind of step that makes this unusable for anyone who does not already know what
|
||||||
|
/// a DLL override is.
|
||||||
|
///
|
||||||
|
/// Persisting the override in the prefix registry removes the manual step entirely: it
|
||||||
|
/// survives restarts and applies to every launch path, including Steam. This mirrors
|
||||||
|
/// what BepInEx documents for Proton (configure the proxy in winecfg rather than the
|
||||||
|
/// environment) and what Proton itself already does in this prefix for other titles.
|
||||||
|
const DLL_OVERRIDE_KEY: &str = r"HKCU\Software\Wine\DllOverrides";
|
||||||
|
const HOOK_DLL_VALUE: &str = "version";
|
||||||
|
const HOOK_DLL_OVERRIDE: &str = "native,builtin";
|
||||||
|
|
||||||
|
/// `reg add` argv that persists the hook's DLL override, native-first with a builtin
|
||||||
|
/// fallback. `/f` makes it idempotent, so this is safe to run on every launch and
|
||||||
|
/// repairs a prefix a player has reset or replaced.
|
||||||
|
fn dll_override_args() -> [&'static str; 10] {
|
||||||
|
[
|
||||||
|
"reg",
|
||||||
|
"add",
|
||||||
|
DLL_OVERRIDE_KEY,
|
||||||
|
"/v",
|
||||||
|
HOOK_DLL_VALUE,
|
||||||
|
"/t",
|
||||||
|
"REG_SZ",
|
||||||
|
"/d",
|
||||||
|
HOOK_DLL_OVERRIDE,
|
||||||
|
"/f",
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Persist the hook's DLL override into the prefix, so the game loads the proxy no
|
||||||
|
/// matter how it is started.
|
||||||
|
///
|
||||||
|
/// Best-effort by design: a failure here is not fatal, because a launch we spawn also
|
||||||
|
/// carries `WINEDLLOVERRIDES`. It is reported in plain language rather than as a Wine
|
||||||
|
/// error, since the player cannot act on the latter.
|
||||||
|
fn ensure_dll_override(profile: &GameProfile, log: &Log) {
|
||||||
|
if profile.wine_prefix.trim().is_empty() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let mut cmd = Command::new(&profile.runner);
|
||||||
|
cmd.args(dll_override_args())
|
||||||
|
.current_dir(&profile.game_dir)
|
||||||
|
.stdout(Stdio::null())
|
||||||
|
.stderr(Stdio::null());
|
||||||
|
for (k, v) in &profile.env {
|
||||||
|
cmd.env(k, v);
|
||||||
|
}
|
||||||
|
cmd.env("WINEPREFIX", &profile.wine_prefix);
|
||||||
|
match cmd.status() {
|
||||||
|
Ok(status) if status.success() => {
|
||||||
|
say(log, "[launcher] game files ready (mod support enabled)");
|
||||||
|
}
|
||||||
|
Ok(_) | Err(_) => say(
|
||||||
|
log,
|
||||||
|
"[launcher] could not pre-enable mod support in the game prefix; \
|
||||||
|
launching anyway (this launch still enables it directly)",
|
||||||
|
),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod override_tests {
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn dll_override_is_persisted_native_first_and_idempotently() {
|
||||||
|
let args = dll_override_args();
|
||||||
|
assert_eq!(args[0], "reg");
|
||||||
|
assert_eq!(args[1], "add");
|
||||||
|
assert_eq!(
|
||||||
|
args[2], r"HKCU\Software\Wine\DllOverrides",
|
||||||
|
"Wine reads overrides from this key; a typo silently leaves the hook unloaded"
|
||||||
|
);
|
||||||
|
assert_eq!(args[4], "version", "the hook ships as a version.dll proxy");
|
||||||
|
assert_eq!(
|
||||||
|
args[8], "native,builtin",
|
||||||
|
"native first so the proxy wins, builtin as fallback so a missing proxy \
|
||||||
|
cannot make the game unlaunchable"
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
args[9], "/f",
|
||||||
|
"idempotent, so running it on every launch repairs a reset prefix"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// The `WINEDLLOVERRIDES` value the game must be started with.
|
/// The `WINEDLLOVERRIDES` value the game must be started with.
|
||||||
///
|
///
|
||||||
/// The hook ships as a `version.dll` proxy inside the game directory, and Proton
|
/// The hook ships as a `version.dll` proxy inside the game directory, and Proton
|
||||||
|
|||||||
+8
-2
@@ -126,8 +126,14 @@ pub fn hook_dll_deployed(game_dir: &Path) -> bool {
|
|||||||
game_dir.join("version.dll").exists()
|
game_dir.join("version.dll").exists()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// The Steam launch options the user needs to paste in to enable the override.
|
/// Steam launch options that enable the hook's DLL override.
|
||||||
/// Proton loads local DLLs named in WINEDLLOVERRIDES ahead of system ones.
|
///
|
||||||
|
/// Kept only as a fallback to show a user who runs the game outside this launcher on
|
||||||
|
/// a prefix we have never prepared. It is NOT the normal path any more: the launcher
|
||||||
|
/// persists the override in the prefix registry itself
|
||||||
|
/// (`game_launch::ensure_dll_override`), which applies to every launch including
|
||||||
|
/// Steam's own Play button. Telling a player to paste launch options is exactly the
|
||||||
|
/// kind of manual step this launcher exists to remove.
|
||||||
pub const STEAM_LAUNCH_OPTIONS: &str = "WINEDLLOVERRIDES=\"version=n,b\" %command%";
|
pub const STEAM_LAUNCH_OPTIONS: &str = "WINEDLLOVERRIDES=\"version=n,b\" %command%";
|
||||||
|
|
||||||
// ── Game launch ───────────────────────────────────────────────────────────────
|
// ── Game launch ───────────────────────────────────────────────────────────────
|
||||||
|
|||||||
Reference in New Issue
Block a user