Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add patch for libcuda.so and implement fix for Star Citizen #136

Merged
Merged
10 changes: 4 additions & 6 deletions gamefixes-umu/umu-starcitizen.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@ def main() -> None:
# eac workaround
util.set_environment('EOS_USE_ANTICHEATCLIENTNULL', '1')

# needed for nvidia vulkan
util.set_environment('WINE_HIDE_NVIDIA_GPU', '1')
# patch libcuda to workaround crashes related to DLSS
# See: https://github.com/jp7677/dxvk-nvapi/issues/174#issuecomment-2227462795
util.patch_libcuda()

# needed for amd vulkan
util.set_environment('dual_color_blend_by_location', 'true')

# allow the RSI Launcher to auto-update itself
# RSI Launcher depends on powershell
util.protontricks('powershell')
89 changes: 89 additions & 0 deletions util.py
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
"""Utilities to make gamefixes easier"""

import configparser
import ctypes.util
from io import TextIOWrapper
import os
import sys
import re
import shutil
import signal
import stat
import tarfile
import zipfile
import subprocess
Expand Down Expand Up @@ -419,6 +421,93 @@ def winedll_override(dll: str, dtype: Literal['n', 'b', 'n,b', 'b,n', '']) -> No
)


def patch_libcuda() -> bool:
"""Patches libcuda to work around games that crash with DLSS due to wonky memory allocation.
ProjectSynchro marked this conversation as resolved.
Show resolved Hide resolved

The patched library is overwritten at every launch and is placed in .cache

Returns true if the library was patched correctly. Otherwise returns false
"""
cache_dir = os.path.expanduser('~/.cache/protonfixes')
os.makedirs(cache_dir, exist_ok=True)

try:
library_base = ctypes.util.find_library('cuda')
if not library_base:
log.warn('libcuda.so not found on the system.')
return False

library_paths = [
'/lib64', '/usr/lib64', '/usr/local/lib64',
'/lib', '/usr/lib', '/usr/local/lib'
]
ProjectSynchro marked this conversation as resolved.
Show resolved Hide resolved

libcuda_path = None
for lib_dir in library_paths:
full_path = os.path.join(lib_dir, library_base)
if os.path.exists(full_path):
# Check if it's a 64-bit ELF file
try:
with open(full_path, 'rb') as f:
e_ident = f.read(5)
if e_ident[:4] != b'\x7fELF':
log.info(f'{full_path} is not an ELF file.')
continue # Not an ELF file
ei_class = e_ident[4]
if ei_class == 2:
# 64-bit ELF
libcuda_path = os.path.abspath(full_path)
break
else:
log.info(f'{full_path} is not a 64-bit ELF file.')
continue # Not 64-bit ELF
except Exception as e:
log.error(f'Error reading {full_path}: {e}')
continue

if not libcuda_path:
log.error(f'libcuda.so found as {library_base}, but not found as a 64-bit library in standard library directories.')
return False

log.info(f'Found 64-bit libcuda.so at: {libcuda_path}')

patched_library = os.path.join(cache_dir, 'libcuda.patched.so')
try:
with open(libcuda_path, 'rb') as f:
binary_data = f.read()
except OSError as e:
log.error(f'Unable to read libcuda.so: {e}')
return False

hex_data = binary_data.hex()
hex_data = hex_data.replace('000000f8ff000000', '000000f8ffff0000')
patched_binary_data = bytes.fromhex(hex_data)
ProjectSynchro marked this conversation as resolved.
Show resolved Hide resolved

try:
with open(patched_library, 'wb') as f:
f.write(patched_binary_data)

# Set permissions to rwxr-xr-x (755)
os.chmod(patched_library, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR | # Owner: rwx
stat.S_IRGRP | stat.S_IXGRP | # Group: r-x
stat.S_IROTH | stat.S_IXOTH) # Others: r-x
log.info(f'Permissions set to rwxr-xr-x for {patched_library}')
except OSError as e:
log.error(f'Unable to write patched libcuda.so to {patched_library}: {e}')
return False

log.info(f'Patched libcuda.so saved to: {patched_library}')

log.info(f'Setting LD_PRELOAD to: {patched_library}')
set_environment('LD_PRELOAD', patched_library)
return True

except Exception as e:
log.error(f'Unexpected error occurred: {e}')
return False



def disable_nvapi() -> None:
"""Disable WINE nv* dlls"""
log.info('Disabling NvAPI')
Expand Down