Compare commits

..

27 commits

Author SHA1 Message Date
gurrgur
98aa86767c
Add Linux HDR example 2024-07-18 15:04:36 +02:00
gurrgur
3ca1d96c9b
Merge pull request #78 from metinc/main
Avoid deleting eldenring.exe while it is running
2024-07-18 14:37:50 +02:00
Metin Çelik
adf3fe57aa Retry deleting eldenring.exe if it is still running 2024-07-14 11:56:59 +02:00
Metin Çelik
e9f39e6ae3 Avoid deleting eldenring.exe while it is locked 2024-07-08 00:46:14 +02:00
Metin Çelik
ebc316f9e3 Use new executable name for Seamless Co-op example 2024-07-07 16:56:18 +02:00
gurrgur
808bf71966
Merge pull request #71 from polsrepo/patch-3
Fix for crash on death with --disable-rune-loss + potential fix for 60Hz fullscreen lock
2024-06-21 16:34:00 +02:00
polsrepo
9c22a9cd1b
Potential fix for crash on death with --disable-rune-loss and 60Hz lock on full screen
Updated the hex pattern in patch.disable_rune_loss to potentially fix the crash on death. Updated the patch.remove_60hz_fullscreen function and hex pattern to potentially fix the 60hz lock in full screen
2024-06-21 12:13:11 +02:00
gurrgur
f2c55ad352
Merge pull request #67 from polsrepo/patch-2
Update README.md for Windows users
2024-06-20 20:08:39 +02:00
polsrepo
05a5425654
Update README.md for Windows users
Added Note 2
2024-06-20 14:01:48 +02:00
gurrgur
2fb795c305
Merge pull request #65 from polsrepo/patch-1
Update er-patcher for v1.12
2024-06-20 13:48:57 +02:00
polsrepo
a388b09bee
Update er-patcher for v1.12
Updated hex pattern for patch.rate, fixed the offset value for patch.remove_60hz_fullscreen
2024-06-20 13:25:04 +02:00
gurrgur
e8be93b2e2
Merge pull request #59 from gurrgur/python-3-9-regression-fix
fix regression on python 3.9
2024-01-12 02:44:23 +01:00
Marcus Gursch
98cf4aeaa8 use old link_to api when running on python 3.9 or older 2024-01-12 02:40:36 +01:00
gurrgur
a82a9d6e01
Merge pull request #56 from gurrgur/python-3-12-compat
Adapt to python 3.12 changes
2023-12-23 14:32:43 +01:00
Marcus Gursch
569917cb4b adapt to python 3.12 changes
Co-authored-by: mtnorthcott <matthew@northcott.nz>
2023-11-20 11:37:46 +01:00
gurrgur
1a3e191ee9
Merge pull request #47 from arminveres/fix/spelling
Fixed vignette spelling
2022-12-27 14:47:44 +01:00
Armin Veres
7faf4c4f25 Fixed vignette spelling 2022-12-27 12:30:36 +01:00
gurrgur
1c4d60e8f7
Merge pull request #44 from IvarWithoutBones/exec-flag
feature: add --executable flag
2022-10-04 20:57:33 +02:00
Ivar Scholten
124fe3c082
feature: add --executable flag
Some mods are launched through a executable different from
`eldenring.exe` or `launch_game_protected.exe`. Because of that
its useful to be able to explicitly specify the path.

Also fixes some linter warnings.
2022-09-30 20:30:08 +02:00
Marcus Gursch
56558f8481 remove --fix-camera option 2022-09-11 14:49:16 +02:00
Marcus Gursch
1d9d2deafc remove --fix-camera option
It is not longer needed since the game now provides a setting for this.
2022-09-11 14:45:38 +02:00
gurrgur
32586d3264
Merge pull request #41 from IvarWithoutBones/disable-rune-loss
Disable rune loss upon death
2022-09-11 14:39:54 +02:00
Ivar Scholten
0f39247ab7
feature: disable rune loss upon death 2022-09-03 18:12:08 +02:00
gurrgur
fd1a1a4f99
Merge pull request #34 from jjgmckenzie/main
Fixes ultrawide for 1.05 with new pattern.
2022-06-16 12:26:02 +02:00
James McKenzie
c3ee974b54 Fixes ultrawide for 1.05 with new pattern. 2022-06-15 12:15:15 -07:00
gurrgur
54f5c17ca5
Merge pull request #28 from joao-pedro-braz/fix-grammar-readme
Fix: Grammar in the README
2022-04-22 18:05:55 +02:00
João Pedro Braz
f6cebbf832
Fix: Grammar in the README
Fixed "Use at own your risk" to "Use it at your own risk"
2022-04-22 11:49:28 -03:00
2 changed files with 91 additions and 42 deletions

View file

@ -15,8 +15,22 @@ 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: `python er-patcher --all --rate 30 --fix-camera -- %command%` - Example:
- 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%`
`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 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.
@ -24,13 +38,14 @@ Note: There might be some distros (e.g. older Ubuntu releases) that launch pytho
## Features ## Features
| Argument | Description | | Argument | Description |
| --------------------------------------- | ------------------------------------------------------------------------------- | | --------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `-r RATE` or `--rate RATE` | Set a custom framerate limit (default: 60). | | `-r RATE` or `--rate RATE` | Set a custom framerate limit (default: 60). |
| `--with-eac` | Run game with EAC (Use at own your risk) | | `-x EXE` or `--executable EXE` | The executable to launch, relative to the games folder.<br>Mutually exclusive with `--with-eac`. |
| `--fix-camera` | Disable camera auto-rotation. | | `--with-eac` | Run game with EAC (Use it at your own risk).<br>Mutually exclusive with `--executable`. |
| `--all` | Enable all options except `--rate` and<br>gameplay changes like `--fix-camera`. | | `--disable-rune-loss` | Disable losing runes upon death. |
| `--all` | Enable all options except `--rate`, `--executable`, and<br>gameplay changes like `--disable-rune-loss`. |
| `-u` or `--ultrawide` | Remove black bars. | | `-u` or `--ultrawide` | Remove black bars. |
| `-v` or `--disable-vigniette` | Remove the vigniette overlay . | | `-v` or `--disable-vignette` | Remove the vignette overlay. |
| `-c` or `--disable-ca` | Disable chromatic abberation. | | `-c` or `--disable-ca` | Disable chromatic abberation. |
| `-a` or `--increase-animation-distance` | Fix low frame rate animations at screen<br>edges or for distant entities. | | `-a` or `--increase-animation-distance` | Fix low frame rate animations at screen<br>edges or for distant entities. |
| `-s` or `--skip-intro` | Skip intro logos at game start. | | `-s` or `--skip-intro` | Skip intro logos at game start. |
@ -45,6 +60,8 @@ 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.
@ -55,7 +72,8 @@ When the game is launched through steam, the tool creates a patched version of `
- frame time limit adjustment - frame time limit adjustment
- black bar removal - black bar removal
- [Flawless Widescreen](https://www.flawlesswidescreen.org) - [Flawless Widescreen](https://www.flawlesswidescreen.org)
- vigniette and ca removal - vignette and ca removal
- animation distance increase - animation distance increase
- [DarkSouls3RemoveIntroScreens](https://github.com/bladecoding/DarkSouls3RemoveIntroScreens): intro logo skip - [DarkSouls3RemoveIntroScreens](https://github.com/bladecoding/DarkSouls3RemoveIntroScreens): intro logo skip
- [EldenRingMods](https://github.com/techiew/EldenRingMods) + [EldenRingFpsUnlockAndMore](https://github.com/uberhalit/EldenRingFpsUnlockAndMore): camera fix - [EldenRingMods](https://github.com/techiew/EldenRingMods) + [EldenRingFpsUnlockAndMore](https://github.com/uberhalit/EldenRingFpsUnlockAndMore)
- disable rune loss

View file

@ -1,6 +1,5 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import os
import sys import sys
import subprocess import subprocess
import argparse import argparse
@ -8,7 +7,23 @@ 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__":
@ -17,23 +32,28 @@ if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Patch Elden Ring executable and launch it without EAC.") parser = argparse.ArgumentParser(description="Patch Elden Ring executable and launch it without EAC.")
parser.add_argument("-r", "--rate", type=int, default=60, help="Modify the frame rate limit (e.g. 30, 120, 165 or whatever).") parser.add_argument("-r", "--rate", type=int, default=60, help="Modify the frame rate limit (e.g. 30, 120, 165 or whatever).")
parser.add_argument("-x", "--executable", action='store', type=str, default="eldenring.exe", help="The executable to launch, relative to the games folder.")
parser.add_argument("--with-eac", action='store_true', help="Run game with EAC (Use at own your risk)") parser.add_argument("--with-eac", action='store_true', help="Run game with EAC (Use at own your risk)")
parser.add_argument("--fix-camera", action='store_true', help="Disable camera auto-rotation.") parser.add_argument("--disable-rune-loss", action='store_true', help="Disable losing runes upon death.")
parser.add_argument("--all", action='store_true', help="Enable all options except rate adjustment and gamplay changes like `--fix-camera`.") parser.add_argument("--all", action='store_true', help="Enable all options except rate adjustment and gamplay changes like `--disable-rune-loss`.")
parser.add_argument("-u", "--ultrawide", action='store_true', help="Removes black bars when using a resolution with an aspect ratio other than 16:9.") parser.add_argument("-u", "--ultrawide", action='store_true', help="Removes black bars when using a resolution with an aspect ratio other than 16:9.")
parser.add_argument("-v", "--disable-vigniette", action='store_true', help="Disables the vigniette overlay.") parser.add_argument("-v", "--disable-vignette", action='store_true', help="Disables the vignette overlay.")
parser.add_argument("-c", "--disable-ca", action='store_true', help="Disables chromatic abberation.") parser.add_argument("-c", "--disable-ca", action='store_true', help="Disables chromatic abberation.")
parser.add_argument("-a", "--increase-animation-distance", action='store_true', help="Increase animation distance.") parser.add_argument("-a", "--increase-animation-distance", action='store_true', help="Increase animation distance.")
parser.add_argument("-s", "--skip-intro", action='store_true', help="Skip intro logos.") parser.add_argument("-s", "--skip-intro", action='store_true', help="Skip intro logos.")
parser.add_argument("-f", "--remove-60hz-fullscreen", action='store_true', help="Remove 60hz lock in fullscreen.") parser.add_argument("-f", "--remove-60hz-fullscreen", action='store_true', help="Remove 60hz lock in fullscreen.")
patch = parser.parse_args(patcher_args) patch = parser.parse_args(patcher_args)
if patch.with_eac and patch.executable != "eldenring.exe":
print("er-patcher: --with-eac is mutually exclusive with --executable")
sys.exit(1)
game_dir = Path(".") game_dir = Path(".")
with open(game_dir / "eldenring.exe", "rb") as f: with open(game_dir / "eldenring.exe", "rb") as f:
exe_hex = f.read().hex() exe_hex = f.read().hex()
if patch.rate != 60 and patch.rate > 0: if patch.rate != 60 and patch.rate > 0:
r_pattern = "c7 43 20 89 88 88 3c eb 43 89 73 18 eb ca 89 73 18".replace(" ", "") r_pattern = "c7 43 1c 89 88 88 3c eb 6d 89 73 18 eb c7 89 73 18".replace(" ", "")
if (res := re.search(r_pattern, exe_hex)) is not None: if (res := re.search(r_pattern, exe_hex)) is not None:
r_addr = res.span()[0] + 6 r_addr = res.span()[0] + 6
r_patch = struct.pack('<f', 1 / patch.rate).hex() r_patch = struct.pack('<f', 1 / patch.rate).hex()
@ -41,17 +61,17 @@ if __name__ == "__main__":
else: else:
print("er-patcher: rate pattern scan failed") print("er-patcher: rate pattern scan failed")
if patch.fix_camera: if patch.disable_rune_loss:
cf_pattern = '0f 29 a6 .. .. .. .. 41 0f 28 cf'.replace(" ", "") rl_pattern = "41 .. 01 48 .. .. e8 .. .. .. .. 48 .. .. .. .. 32 c0".replace(" ", "")
if (res := re.search(cf_pattern, exe_hex)) is not None: if (res := re.search(rl_pattern, exe_hex)) is not None:
cf_addr = res.span()[0] rl_addr = res.span()[0] + 12
cf_patch = "90 90 90 90 90 90 90".replace(" ", "") rl_patch = "90 90 90 90 90".replace(" ", "") # NOP
exe_hex = exe_hex[:cf_addr] + cf_patch + exe_hex[cf_addr + len(cf_patch):] exe_hex = exe_hex[:rl_addr] + rl_patch + exe_hex[rl_addr + len(rl_patch):]
else: else:
print("er-patcher: fix_camera pattern scan failed") print("er-patcher: disable rune loss pattern scan failed")
if patch.ultrawide or patch.all: if patch.ultrawide or patch.all:
uw_pattern = "74 50 .. 8b .. .. dc 03 00 00 .. 85 .. 74 .. .. 8b .. .. 0f af".replace(" ", "") uw_pattern = "74 4f 45 8b 94 cc".replace(" ", "")
if (res := re.search(uw_pattern, exe_hex)) is not None: if (res := re.search(uw_pattern, exe_hex)) is not None:
uw_addr = res.span()[0] uw_addr = res.span()[0]
uw_patch = "eb" uw_patch = "eb"
@ -59,14 +79,14 @@ if __name__ == "__main__":
else: else:
print("er-patcher: ultrawide pattern scan failed") print("er-patcher: ultrawide pattern scan failed")
if patch.disable_vigniette or patch.all: if patch.disable_vignette or patch.all:
v_pattern = 'f3 0f 10 .. .. f3 0f 59 .. .. .. .. .. e8 .. .. .. .. f3 41 0f .. .. f3 45 0f .. .. 4c 8d .. .. .. .. .. .. 48'.replace(" ", "") v_pattern = 'f3 0f 10 .. .. f3 0f 59 .. .. .. .. .. e8 .. .. .. .. f3 41 0f .. .. f3 45 0f .. .. 4c 8d .. .. .. .. .. .. 48'.replace(" ", "")
if (res := re.search(v_pattern, exe_hex)) is not None: if (res := re.search(v_pattern, exe_hex)) is not None:
v_addr = res.span()[0] + 46 v_addr = res.span()[0] + 46
v_patch = "f3 0f 5c c0 90".replace(" ", "") # SUBSS XMM0,XMM0; NOP; all NOP does work too v_patch = "f3 0f 5c c0 90".replace(" ", "") # SUBSS XMM0,XMM0; NOP; all NOP does work too
exe_hex = exe_hex[:v_addr] + v_patch + exe_hex[v_addr + len(v_patch):] exe_hex = exe_hex[:v_addr] + v_patch + exe_hex[v_addr + len(v_patch):]
else: else:
print("er-patcher: disable_vigniette pattern scan failed") print("er-patcher: disable_vignette pattern scan failed")
if patch.disable_ca or patch.all: if patch.disable_ca or patch.all:
ca_pattern = "0f 11 43 60 48 8d 8b 80 00 00 00 0f 10 87 a0 00 00 00 0f 11 41 f0 48 8d 87 b0 00 00 00 0f 10 08 0f 11 09".replace(" ", "") ca_pattern = "0f 11 43 60 48 8d 8b 80 00 00 00 0f 10 87 a0 00 00 00 0f 11 41 f0 48 8d 87 b0 00 00 00 0f 10 08 0f 11 09".replace(" ", "")
@ -98,15 +118,23 @@ 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 = "c7 45 ef 3c 00 00 00".replace(" ", "") fs_pattern = "eb .. c7 .. .. 3c 00 00 00 c7 .. .. 01 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] + 6 fs_addr = res.span()[0] + 10
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()
@ -130,12 +158,15 @@ if __name__ == "__main__":
if f.name in ["eldenring.exe", "er-patcher"]: if f.name in ["eldenring.exe", "er-patcher"]:
continue continue
if not (game_dir_patched / f).is_file(): if not (game_dir_patched / f).is_file():
if sys.version_info.minor >= 10:
(game_dir_patched / f).hardlink_to(f)
else:
f.link_to(game_dir_patched / f) f.link_to(game_dir_patched / f)
# start patched exe directly to avoid EAC # start patched exe directly to avoid EAC
steam_cmd = sys.argv[1 + sys.argv.index("--"):] steam_cmd = sys.argv[1 + sys.argv.index("--"):]
steam_cmd[-1] = Path(steam_cmd[-1]).parent.absolute() / game_dir_patched / ("start_protected_game.exe" if patch.with_eac else "eldenring.exe") 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())
# cleanup # try to remove game_dir_patched
rmtree(game_dir_patched) cleanup(game_dir_patched)