Compare commits

..

No commits in common. "main" and "v1.12-1" have entirely different histories.

2 changed files with 9 additions and 48 deletions

View file

@ -15,22 +15,9 @@ A tool aimed at enhancing the experience when playing the game on linux through
1. Copy the file `er-patcher` to the game directory. 1. Copy the file `er-patcher` to the game directory.
2. In steam, set the game launch options to `python er-patcher ARGS -- %command%` See [Features](#features) for available options. 2. In steam, set the game launch options to `python er-patcher ARGS -- %command%` See [Features](#features) for available options.
- Example: - Example: `python er-patcher --all --rate 30 --disable-rune-loss -- %command%`
- Example using the [seamless co-op](https://www.nexusmods.com/eldenring/mods/510) mod: `python er-patcher --all --executable launch_elden_ring_seamlesscoop.exe -- %command%`
`python er-patcher --all --rate 30 --disable-rune-loss -- %command%` - Example using [MangoHud](https://github.com/flightlessmango/MangoHud) and wine fullscreen FSR: `python er-patcher --rate 144 -uvca -- env WINE_FULLSCREEN_FSR=1 MANGOHUD=1 MANGOHUD_CONFIG=histogram %command%`
- Example using the [Seamless Co-op](https://www.nexusmods.com/eldenring/mods/510) mod:
`python er-patcher --all --executable ersc_launcher.exe -- %command%`
- Example using [MangoHud](https://github.com/flightlessmango/MangoHud) and wine fullscreen FSR:
`python er-patcher --rate 144 -uvca -- env WINE_FULLSCREEN_FSR=1 MANGOHUD=1 MANGOHUD_CONFIG=histogram %command%`
- Example for enabling HDR using gamescope on Linux (reported to work on Plasma 6.1):
`ENABLE_GAMESCOPE_WSI=1 DXVK_HDR=1 gamescope -W 3440 -H 1440 -f -r 165 --hdr-enabled -- python er-patcher --all --rate 165 -- %command%`
3. Launch the game through steam. `er-patcher` automatically launches a patched version of `eldenring.exe` with EAC disabled. 3. Launch the game through steam. `er-patcher` automatically launches a patched version of `eldenring.exe` with EAC disabled.
Note: There might be some distros (e.g. older Ubuntu releases) that launch python 2 instead of 3 when running `python`. In that case you'll need to replace `python` with `python3` in the launch option line. Note: There might be some distros (e.g. older Ubuntu releases) that launch python 2 instead of 3 when running `python`. In that case you'll need to replace `python` with `python3` in the launch option line.
@ -60,8 +47,6 @@ The patcher works just as well on windows. The following launch option line work
Note: This spawns a python console which will close by itself after the game has finished running. If you find this annoying you can try using `pythonw` instead. In any case `python` needs to be in PATH for windows to find it. Note: This spawns a python console which will close by itself after the game has finished running. If you find this annoying you can try using `pythonw` instead. In any case `python` needs to be in PATH for windows to find it.
Note 2: Ensure Vertical Sync is turned off for Elden Ring in Nvidia Control Panel / AMD Radeon Software / Intel Graphics Command Center, otherwise the custom framerate limit feature won't work.
## How it works ## How it works
When the game is launched through steam, the tool creates a patched version of `eldenring.exe` in a temporary subdirectory while leaving the original intact. As long the flag `--with-eac` is not set, the tool modifies the steam launch command to launch the patched executable instead of `start_protected_game.exe`, thefore ensuring that the patched exe is never run with EAC enabled. After the game is closed, the patched executable is removed. When the game is launched through steam, the tool creates a patched version of `eldenring.exe` in a temporary subdirectory while leaving the original intact. As long the flag `--with-eac` is not set, the tool modifies the steam launch command to launch the patched executable instead of `start_protected_game.exe`, thefore ensuring that the patched exe is never run with EAC enabled. After the game is closed, the patched executable is removed.

View file

@ -7,23 +7,7 @@ from pathlib import Path
import struct import struct
import re import re
from shutil import rmtree from shutil import rmtree
import os
import time
def cleanup(game_dir_patched):
if game_dir_patched.exists():
eldenring_path = game_dir_patched / "eldenring.exe"
while eldenring_path.exists():
try:
os.remove(eldenring_path)
break
except PermissionError:
# eldenring.exe is still running, retry in 3 s
time.sleep(3)
except Exception as e:
print(f"er-patcher: could not delete {eldenring_path}: {e}")
break
rmtree(game_dir_patched)
if __name__ == "__main__": if __name__ == "__main__":
@ -62,9 +46,9 @@ if __name__ == "__main__":
print("er-patcher: rate pattern scan failed") print("er-patcher: rate pattern scan failed")
if patch.disable_rune_loss: if patch.disable_rune_loss:
rl_pattern = "41 .. 01 48 .. .. e8 .. .. .. .. 48 .. .. .. .. 32 c0".replace(" ", "") rl_pattern = "b0 01 .. 8b .. e8 .. .. .. .. .. 8b .. .. .. 32 c0 .. 83 .. 28 c3".replace(" ", "")
if (res := re.search(rl_pattern, exe_hex)) is not None: if (res := re.search(rl_pattern, exe_hex)) is not None:
rl_addr = res.span()[0] + 12 rl_addr = res.span()[0] + 6
rl_patch = "90 90 90 90 90".replace(" ", "") # NOP rl_patch = "90 90 90 90 90".replace(" ", "") # NOP
exe_hex = exe_hex[:rl_addr] + rl_patch + exe_hex[rl_addr + len(rl_patch):] exe_hex = exe_hex[:rl_addr] + rl_patch + exe_hex[rl_addr + len(rl_patch):]
else: else:
@ -118,23 +102,15 @@ if __name__ == "__main__":
print("er-patcher: skip_intro pattern scan failed") print("er-patcher: skip_intro pattern scan failed")
if patch.remove_60hz_fullscreen or patch.all: if patch.remove_60hz_fullscreen or patch.all:
fs_pattern = "eb .. c7 .. .. 3c 00 00 00 c7 .. .. 01 00 00 00".replace(" ", "") fs_pattern = "c7 45 ef 3c 00 00 00".replace(" ", "")
if (res := re.search(fs_pattern, exe_hex)) is not None: if (res := re.search(fs_pattern, exe_hex)) is not None:
fs_addr = res.span()[0] + 10 fs_addr = res.span()[0] + 3
fs_patch = "00" fs_patch = "00"
exe_hex = exe_hex[:fs_addr] + fs_patch + exe_hex[fs_addr + len(fs_patch):] exe_hex = exe_hex[:fs_addr] + fs_patch + exe_hex[fs_addr + len(fs_patch):]
fs_addr_2 = res.span()[0] + 24
fs_patch_2 = "00"
exe_hex = exe_hex[:fs_addr_2] + fs_patch_2 + exe_hex[fs_addr_2 + len(fs_patch_2):]
else: else:
print("er-patcher: remove_60hz_fullscreen pattern scan failed") print("er-patcher: remove_60hz_fullscreen pattern scan failed")
game_dir_patched = Path("er-patcher-tmp") game_dir_patched = Path("er-patcher-tmp")
# make sure a fresh directory is used
cleanup(game_dir_patched)
if not game_dir_patched.is_dir(): if not game_dir_patched.is_dir():
game_dir_patched.mkdir() game_dir_patched.mkdir()
@ -168,5 +144,5 @@ if __name__ == "__main__":
steam_cmd[-1] = Path(steam_cmd[-1]).parent.absolute() / game_dir_patched / ("start_protected_game.exe" if patch.with_eac else patch.executable) steam_cmd[-1] = Path(steam_cmd[-1]).parent.absolute() / game_dir_patched / ("start_protected_game.exe" if patch.with_eac else patch.executable)
subprocess.run(steam_cmd, cwd=steam_cmd[-1].parent.absolute()) subprocess.run(steam_cmd, cwd=steam_cmd[-1].parent.absolute())
# try to remove game_dir_patched # cleanup
cleanup(game_dir_patched) rmtree(game_dir_patched)