Compare commits

..

60 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
gurrgur
b87fa70015
Merge pull request #25 from joao-pedro-braz/dev-with-eac
Flag to allow the game to run with EAC
2022-04-22 10:55:03 +02:00
João Pedro Rodrigues Prates Braz
6c4be1ae92 Revert "Fix: Major bug where the flag 'with-eac' defaults to true"
This reverts commit ebcf78c9bb.
2022-04-21 18:38:38 -03:00
João Pedro Rodrigues Prates Braz
b1b2af55af Feat: Improved README 2022-04-21 18:34:44 -03:00
João Pedro Rodrigues Prates Braz
ebcf78c9bb Fix: Major bug where the flag 'with-eac' defaults to true 2022-04-21 18:34:05 -03:00
João Pedro Rodrigues Prates Braz
33b88872f8 Feat: Updated the README to include the 'with-eac' arg 2022-04-21 17:21:54 -03:00
gurrgur
d07b112f06
Merge pull request #26 from joao-pedro-braz/dev-ultrawide-fix
Update Ultrawide fix to version 1.04
2022-04-21 21:56:35 +02:00
João Pedro Rodrigues Prates Braz
0afbe0c4f1 Feat: Include 'start_protected_game.exe' in the tmp folder 2022-04-21 01:02:55 -03:00
João Pedro Rodrigues Prates Braz
e2f0ab53cf Feat: Updated Ultrawide fix to version 1.04 2022-04-21 00:56:17 -03:00
João Pedro Rodrigues Prates Braz
771663eabf Feat: Added option to run game with EAC enabled 2022-04-21 00:54:22 -03:00
Marcus Gursch
0ec730fe89 readme: update recommended launch option line to prevent file permission errors 2022-03-29 21:17:43 +02:00
Marcus Gursch
100c4561b7 fix whitespace 2022-03-29 10:26:33 +02:00
Marcus Gursch
f49096c2c6 feature: gracefully fail pattern scanning and print helpful error messages
refactor: use regex pattern scanning everywhere
2022-03-28 21:26:32 +02:00
Marcus Gursch
7ae9c59fcc update animation fix with proper pattern scanning 2022-03-28 21:10:14 +02:00
gurrgur
6ccfe85cdd
Merge pull request #16 from gurrgur/dev-mod-compatibility
Improve Mod Compatibility
2022-03-26 16:04:11 +01:00
Marcus Gursch
a531256c0a use old hard link api for python 3.8 compatibility 2022-03-25 20:47:52 +01:00
Marcus Gursch
62cb574ced improve mod compatibility 2022-03-25 20:24:26 +01:00
gurrgur
83d3da7f88
readme: make feature table slimmer 2022-03-23 12:51:49 +01:00
Marcus Gursch
e692e73890 improve readme 2022-03-23 12:49:44 +01:00
Marcus Gursch
f254eb37b9 feature: add camera fix 2022-03-23 12:49:18 +01:00
Marcus Gursch
6396ebb6ea feature: add option for enabling all patches at once 2022-03-23 12:06:37 +01:00
Marcus Gursch
7f00e876a5 feature: add intro skip patch 2022-03-23 12:00:01 +01:00
gurrgur
635a80b64b
Merge pull request #10 from bright-side-99/main
support Ubuntu 20.04 lts (Python 3.8.10):
2022-03-22 17:28:01 +01:00
bright-side-99
92f1a6e6ee change dependency to Python >= 3.8.xx 2022-03-20 21:55:15 +01:00
andy
6b163799c6
remove unnecessary newline 2022-03-20 21:51:46 +01:00
bright-side-99
ef7ffd2dd2 support Python 3.8.10:
replace argparse.BooleanOptionalAction
as it requires python 3.9, ubuntu 20.04 lts based distros are still stuck on Python 3.8.xx
2022-03-20 21:43:17 +01:00
gurrgur
5de03c9d58
readme: fix formatting 2022-03-17 16:00:32 +01:00
gurrgur
00db8431ae
readme: add note on python version requirement 2022-03-17 16:00:01 +01:00
Marcus Gursch
eab689a6cc make vigniette removal work with game v1.03 2022-03-17 15:52:09 +01:00
gurrgur
01a6e8e910
Merge pull request #2 from DinCahill/fix/save-memory
fix: save memory
2022-03-09 10:39:54 +01:00
Donal Cahill
9dc4e6d6da fix: save memory 2022-03-09 03:30:31 +00:00
gurrgur
db368aa371
readme: mention windows support in description 2022-03-08 17:34:00 +01:00
Marcus Gursch
2f84b18edb readme: add usage info for fullscreen display rate unlock 2022-03-08 17:11:07 +01:00
Marcus Gursch
29fefd606f feature: add option for removing 60Hz limit in fullscreen display modes (author: Lyall) 2022-03-08 17:09:42 +01:00
2 changed files with 196 additions and 64 deletions

View file

@ -1,40 +1,70 @@
# Elden Ring Proton Patcher
A tool aimed at enhancing the experience when playing the game on linux through proton.
A tool aimed at enhancing the experience when playing the game on linux through proton or natively on windows.
## Warning
**This tool is based on patching the game executable through hex-edits. However it is done in a safe and non-destructive way, that ensures the patched executable is never run with EAC enabled. Use at your own risk!**
**This tool is based on patching the game executable through hex-edits. However it is done in a safe and non-destructive way, that ensures the patched executable is never run with EAC enabled (unless explicity told to do so). Use at your own risk!**
## Features
## Dependencies
- set custom frame time limits (e.g. 30, 90, 165, ...)
- disable black borders when using resolutions with an aspect ratio other than 16:9 (e.g. ultrawide).
- vigniette removal
- Python >= 3.8
## Usage
1. Copy the file `er-patcher` to the game directory.
2. In steam, set the game launch options to `./er-patcher ARGS -- %command%` where ARGS is replaced with a combination of
- `-r RATE` or `--rate RATE` for setting a custom framerate cap (default: 60)
- `-u` or `--ultrawide` for removing black bars
- `-v` or `--disable-vigniette` for removing the vigniette overlay
- `-c` or `--disable-ca` for disabling chromatic abberation
- `-a` or `--increase-animation-distance` for fixing low frame rate animations at screen edges or for distant entities.
- Example: `./er-patcher --rate 30 -uavc -- %command%`
- Example with mangohud and wine fullscreen fsr: `./er-patcher --rate 144 -uvca -- env WINE_FULLSCREEN_FSR=1 MANGOHUD=1 MANGOHUD_CONFIG=histogram %command%`
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 --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.
### Windows
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.
It also work just as well on windows. The only difference is, that you need to run the script via your Python 3 installation. The following launch option line works in case you installed Python from Microsoft Store:
## Features
> `python er-patcher --rate 165 -uvca -- %command%`
| Argument | Description |
| --------------------------------------- | --------------------------------------------------------------------------------------------------------- |
| `-r RATE` or `--rate RATE` | Set a custom framerate limit (default: 60). |
| `-x EXE` or `--executable EXE` | The executable to launch, relative to the games folder.<br>Mutually exclusive with `--with-eac`. |
| `--with-eac` | Run game with EAC (Use it at your own risk).<br>Mutually exclusive with `--executable`. |
| `--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. |
| `-v` or `--disable-vignette` | Remove the vignette overlay. |
| `-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. |
| `-s` or `--skip-intro` | Skip intro logos at game start. |
| `-f` or `--remove-60hz-fullscreen` | Remove the 60Hz limit in fullscreen<br>mode (not needed with proton). |
## Windows Support
The patcher works just as well on windows. The following launch option line works in case you e.g. installed Python from Microsoft Store:
> `python er-patcher --rate 165 --all -- %command%`
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
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. The tool then modifies the steam launch command to launch the patched executable instead of `start_protected_game.exe`. This ensures 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.
## Credits
@ -42,5 +72,8 @@ When the game is launched through steam, the tool creates a patched version of `
- frame time limit adjustment
- black bar removal
- [Flawless Widescreen](https://www.flawlesswidescreen.org)
- vigniette and ca removal
- vignette and ca removal
- animation distance increase
- [DarkSouls3RemoveIntroScreens](https://github.com/bladecoding/DarkSouls3RemoveIntroScreens): intro logo skip
- [EldenRingMods](https://github.com/techiew/EldenRingMods) + [EldenRingFpsUnlockAndMore](https://github.com/uberhalit/EldenRingFpsUnlockAndMore)
- disable rune loss

View file

@ -1,73 +1,172 @@
#!/usr/bin/env python3
import os
import sys
import subprocess
import argparse
from pathlib import Path
import struct
import re
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__":
patcher_args = sys.argv[1:sys.argv.index("--")]
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("-u", "--ultrawide", action=argparse.BooleanOptionalAction, help="Removes black bars when using a resolution with an aspect ratio other than 16:9.")
parser.add_argument("-v", "--disable-vigniette", action=argparse.BooleanOptionalAction, help="Disables the vigniette overlay.")
parser.add_argument("-c", "--disable-ca", action=argparse.BooleanOptionalAction, help="Disables chromatic abberation.")
parser.add_argument("-a", "--increase-animation-distance", action=argparse.BooleanOptionalAction, help="Increase animation distance.")
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("--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 `--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("-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("-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("-f", "--remove-60hz-fullscreen", action='store_true', help="Remove 60hz lock in fullscreen.")
patch = parser.parse_args(patcher_args)
exe_name = Path("eldenring.exe")
with open(exe_name, "rb") as f:
exe = f.read()
exe_hex = exe.hex()
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(".")
with open(game_dir / "eldenring.exe", "rb") as f:
exe_hex = f.read().hex()
if patch.rate != 60 and patch.rate > 0:
exe_hex = exe_hex.replace(
"c743208988883ceb43897318ebca897318",
"c743208988883ceb43897318ebca897318".replace(
"8988883c", struct.pack('<f', 1 / patch.rate).hex()
)
)
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:
r_addr = res.span()[0] + 6
r_patch = struct.pack('<f', 1 / patch.rate).hex()
exe_hex = exe_hex[:r_addr] + r_patch + exe_hex[r_addr + len(r_patch):]
else:
print("er-patcher: rate pattern scan failed")
if patch.ultrawide:
exe_hex = exe_hex.replace(
"8b0185c07442448b5904",
"8b0185c0eb42448b5904"
)
if patch.disable_rune_loss:
rl_pattern = "41 .. 01 48 .. .. e8 .. .. .. .. 48 .. .. .. .. 32 c0".replace(" ", "")
if (res := re.search(rl_pattern, exe_hex)) is not None:
rl_addr = res.span()[0] + 12
rl_patch = "90 90 90 90 90".replace(" ", "") # NOP
exe_hex = exe_hex[:rl_addr] + rl_patch + exe_hex[rl_addr + len(rl_patch):]
else:
print("er-patcher: disable rune loss pattern scan failed")
if patch.disable_vigniette:
exe_hex = exe_hex.replace(
"f30f590514a76e01f3440f5cc8f3450f5ecb",
"f30f590524b56e01f3440f5cc8f3450f5ecb"
)
if patch.ultrawide or patch.all:
uw_pattern = "74 4f 45 8b 94 cc".replace(" ", "")
if (res := re.search(uw_pattern, exe_hex)) is not None:
uw_addr = res.span()[0]
uw_patch = "eb"
exe_hex = exe_hex[:uw_addr] + uw_patch + exe_hex[uw_addr + len(uw_patch):]
else:
print("er-patcher: ultrawide pattern scan failed")
if patch.disable_ca:
ca_addr = 94 + exe_hex.index("0f114360488d8b800000000f1087a00000000f1141f0488d87b00000000f10080f1109")
if exe_hex[ca_addr:ca_addr + 8] == "0f114920":
exe_hex = exe_hex[:ca_addr] + "660fefc9" + exe_hex[ca_addr + 8:] # PXOR XMM1,XMM1
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(" ", "")
if (res := re.search(v_pattern, exe_hex)) is not None:
v_addr = res.span()[0] + 46
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):]
else:
print("er-patcher: disable_vignette pattern scan failed")
if patch.increase_animation_distance:
# DIVSS XMM1,dword ptr [R12 + 0x54] -> XORPS XMM1,XMM1; PXOR XMM1,XMM1
exe_hex = exe_hex.replace(
"e82b309c010f28f80f28c6e820359c01f30f5ef80f28cff3410f5e4c2454",
"e82b309c010f28f80f28c6e820359c01f30f5ef80f28cf0f57c9660fefc9"
)
patched_exe_dir = Path("./er-patcher-tmp")
if not patched_exe_dir.is_dir():
patched_exe_dir.mkdir()
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(" ", "")
if (res := re.search(ca_pattern, exe_hex)) is not None:
ca_addr = res.span()[0] + 94
ca_orig = "0f 11 49 20".replace(" ", "")
ca_patch = "66 0f ef c9".replace(" ", "") # PXOR XMM1,XMM1
if exe_hex[ca_addr:ca_addr + len(ca_patch)] == ca_orig:
exe_hex = exe_hex[:ca_addr] + ca_patch + exe_hex[ca_addr + len(ca_patch):]
else:
print("er-patcher: disable_ca pattern scan failed")
with open(patched_exe_dir / exe_name, "wb") as f:
if patch.increase_animation_distance or patch.all:
iad_pattern = "e8 .. .. .. .. 0f 28 .. 0f 28 .. e8 .. .. .. .. f3 0f .. .. 0f 28 .. f3 41 0f 5e 4c 24 54".replace(" ", "")
if (res := re.search(iad_pattern, exe_hex)) is not None:
iad_addr = res.span()[0] + 46
iad_patch = "0f 57 c9 66 0f ef c9".replace(" ", "") # DIVSS XMM1,dword ptr [R12 + 0x54] -> XORPS XMM1,XMM1; PXOR XMM1,XMM1
exe_hex = exe_hex[:iad_addr] + iad_patch + exe_hex[iad_addr + len(iad_patch):]
else:
print("er-patcher: increase_animation_distance pattern scan failed")
if patch.skip_intro or patch.all:
si_pattern = "80 bf b8 00 00 00 00 74 53 48".replace(" ", "")
if (res := re.search(si_pattern, exe_hex)) is not None:
si_addr = res.span()[0] + 14
si_patch = "90 90".replace(" ", "")
exe_hex = exe_hex[:si_addr] + si_patch + exe_hex[si_addr + len(si_patch):]
else:
print("er-patcher: skip_intro pattern scan failed")
if patch.remove_60hz_fullscreen or patch.all:
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:
fs_addr = res.span()[0] + 10
fs_patch = "00"
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:
print("er-patcher: remove_60hz_fullscreen pattern scan failed")
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():
game_dir_patched.mkdir()
with open(game_dir_patched / "eldenring.exe", "wb") as f:
f.write(bytes.fromhex(exe_hex))
del exe_hex
# recreate game directory tree in game_dir_patched
game_dirs = [d for d in game_dir.rglob("*") if d.is_dir()]
for d in game_dirs:
if d == game_dir_patched:
continue
if not (game_dir_patched / d).is_dir():
(game_dir_patched / d).mkdir(parents=True)
# hard link game files to game_dir_patched; symbolic links would be easier
# to handle but by default windows 10 doesn't allow them
game_files = [f for f in game_dir.rglob("*") if f.is_file()]
for f in game_files:
if f.name in ["eldenring.exe", "er-patcher"]:
continue
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)
# start patched exe directly to avoid EAC
steam_cmd = sys.argv[1 + sys.argv.index("--"):]
steam_cmd[-1] = Path(steam_cmd[-1]).parent.absolute() / patched_exe_dir / exe_name
subprocess.run(steam_cmd)
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())
os.remove(patched_exe_dir / exe_name)
os.rmdir(patched_exe_dir)
# try to remove game_dir_patched
cleanup(game_dir_patched)