|
""" Game fix for Fallout 3 - Game of the Year Edition |
|
""" |
|
# pylint: disable=C0103 |
|
from protonfixes import util |
|
from protonfixes.logger import log |
|
|
|
import os |
|
import sys |
|
import shutil |
|
import urllib.request |
|
import zipfile |
|
import time |
|
|
|
import subprocess |
|
|
|
try: |
|
from http.cookiejar import CookieJar |
|
except ImportError: |
|
from cookielib import CookieJar |
|
|
|
|
|
def download_gdrive(fileid): |
|
cj = CookieJar() |
|
opener = urllib.request.build_opener(urllib.request.HTTPCookieProcessor(cj)) |
|
opener.addheaders.append(('Range', 'bytes=0-')) |
|
|
|
confirm_code = None |
|
|
|
req = opener.open('https://drive.google.com/uc?export=download&id=' + fileid) |
|
print('HTTP ' + str(req.code)) |
|
|
|
for cookie in list(cj): |
|
if cookie.name.startswith('download_warning_'): |
|
confirm_code = cookie.value |
|
break |
|
|
|
if not confirm_code: |
|
return req |
|
else: |
|
req.close() |
|
|
|
return opener.open('https://drive.google.com/uc?export=download&confirm=' + confirm_code + '&id=' + fileid) |
|
|
|
|
|
def main(): |
|
|
|
ENB = bool(int(util.protonmain.g_session.env.get('ENB', '0'))) |
|
LAUNCHER = bool(int(util.protonmain.g_session.env.get('LAUNCHER', '1'))) |
|
|
|
util.set_environment('PROTON_FORCE_LARGE_ADDRESS_AWARE', '1') |
|
|
|
# util.disable_esync() |
|
|
|
util.winedll_override('winegstreamer', '') |
|
|
|
# Fixes radio |
|
util.protontricks('quartz') |
|
|
|
# to load d3d9-wrapper (fps limiter) or ENB |
|
util.winedll_override('d3d9', 'n,b') |
|
|
|
# required by ENB: |
|
if ENB: |
|
# util.protontricks('d3dx9_26') |
|
# util.protontricks('d3dx9_40') |
|
util.protontricks('d3dx9_43') |
|
util.protontricks('d3dcompiler_43') |
|
# util.protontricks('d3dcompiler_47') |
|
# util.protontricks('d3dx9_37') |
|
# util.protontricks('d3dx9_38') |
|
util.winedll_override('dxgi', 'n,b') |
|
|
|
try: |
|
os.makedirs(os.path.join(util.get_game_install_path(), 'Saves')) |
|
except: |
|
pass |
|
try: |
|
os.makedirs(os.path.join(util.protonprefix(), 'drive_c/users/steamuser/My Documents/My Games/Fallout3')) |
|
except: |
|
pass |
|
|
|
try: |
|
os.symlink( |
|
os.path.join(util.get_game_install_path(), 'Saves'), |
|
os.path.join(util.protonprefix(), 'drive_c/users/steamuser/My Documents/My Games/Fallout3/Saves') |
|
) |
|
except: |
|
pass |
|
|
|
enb_opts = ''' |
|
[Display] |
|
iMultiSample=0 |
|
iWaterMultisamples=0 |
|
bTransparencyMultisampling=0 |
|
iMaxAnisotropy=0 |
|
|
|
[Water] |
|
bUseWaterDisplacements=0 |
|
''' |
|
|
|
user_opts = ''' |
|
[General] |
|
bUseThreadedAI=1 |
|
iNumHWThreads=2 |
|
bUseThreadedBlood=1 |
|
bUseThreadedMorpher=1 |
|
bUseThreadedTempEffects=1 |
|
bUseThreadedParticleSystem=1 |
|
bUseMultiThreadedFaceGen=1 |
|
bUseMultiThreadedTrees=1 |
|
iPreloadSizeLimit=104857600 |
|
bPreemptivelyUnloadCells=1 |
|
|
|
[BackgroundLoad] |
|
bSelectivePurgeUnusedOnFastTravel=1 |
|
|
|
[HAVOK] |
|
iNumHavokThreads=4 |
|
|
|
[Display] |
|
fDefaultFOV=85 |
|
fDefaultWorldFOV=85 |
|
fDefault1stPersonFOV=65 |
|
fPipboy1stPersonFOV=50 |
|
iPresentInterval=1 |
|
bFull Screen=1 |
|
|
|
[RenderedTerminal] |
|
fRenderedTerminalFOV=0.13 |
|
|
|
[Audio] |
|
bMultiThreadAudio=0 |
|
bEnableAudioCache=1 |
|
iAudioCacheSize=4096 |
|
iMaxSizeForCachedSound=512 |
|
iRadioUpdateInterval=1024 |
|
|
|
[Controls] |
|
fForegroundMouseMult=0 |
|
fForegroundMouseAccelBase=0 |
|
fForegroundMouseAccelTop=0 |
|
fForegroundMouseBase=0 |
|
bBackground Mouse=0 |
|
|
|
[Archive] |
|
bInvalidateOlderFiles=1 |
|
|
|
[GamePlay] |
|
bSaveOnInteriorExteriorSwitch=0 |
|
bSaveOnTravel=0 |
|
bSaveOnWait=0 |
|
bSaveOnRest=0 |
|
''' |
|
|
|
util.set_ini_options(user_opts, 'Fallout_default.ini', base_path='game') |
|
log.info('Updated Fallout_default.ini') |
|
|
|
active_plugins = ''' |
|
# This file is used by Fallout 3 to keep track of your downloaded content. |
|
Anchorage.esm |
|
BrokenSteel.esm |
|
Compiled fixes for Fallout 3 ( compatible with UOFO3P ).esp |
|
djazz - JuryStStation02 train fix.esp |
|
Fallout3.esm |
|
LilMac.esp |
|
PointLookout.esm |
|
Safe - Safe Regulator HQ.esp |
|
ThePitt.esm |
|
Tinker Joe fix for UOF3P.esp |
|
Unofficial Fallout 3 Patch.esm |
|
ZebraCarbine.esp |
|
Zeta.esm |
|
''' |
|
|
|
local_settings_path = os.path.join(util.protonprefix(), 'drive_c/users/steamuser/Local Settings/Application Data/Fallout3') |
|
try: |
|
os.makedirs(local_settings_path) |
|
except: |
|
pass |
|
if not os.path.exists(os.path.join(local_settings_path, 'plugins.txt')): |
|
with open(os.path.join(local_settings_path, 'plugins.txt'), 'w') as f: |
|
f.write(active_plugins) |
|
log.info('Wrote plugins.txt') |
|
|
|
if not os.path.islink('Fallout3.exe') or not os.path.islink('d3d9.dll'): |
|
log.info('Downloading modpack and utilities...') |
|
try: |
|
req = None |
|
err2 = None |
|
try: |
|
fileid = '1KhbJV7GBLE0uYEi8Ou-TCl4lbo7lmM7X' |
|
req = download_gdrive(fileid) |
|
log.info('Downloading from Google Drive') |
|
except Exception as err: |
|
err2 = err |
|
log.warn('Google Drive error! ' + str(err)) |
|
log.info('Using mirror...') |
|
try: |
|
modpack_url = 'https://djazz.se/nas/games/fallout3/fallout-3-goty-modpack-proton.zip' |
|
req = urllib.request.urlopen(modpack_url) |
|
log.info('Downloading from djazz.se') |
|
except Exception as err: |
|
err2 = err |
|
log.warn('Download error! Is djazz.se online? ' + str(err)) |
|
if not req: |
|
raise Exception('Error: Unable to download. ' + str(err2)) |
|
with req: |
|
totalsize = 0 |
|
try: |
|
totalsize = int(req.getheader('Content-Length')) |
|
except Exception: |
|
try: |
|
totalsize = int(req.getheader('Content-Range').partition('/')[2]) |
|
except Exception: |
|
pass |
|
|
|
with open('modpack-proton.zip', 'wb') as fp: |
|
progress = 0 |
|
while True: |
|
chunk = req.read(8 * 1024 * 1024) |
|
if not chunk: |
|
break |
|
fp.write(chunk) |
|
progress += len(chunk) |
|
if totalsize: |
|
log.info('Download progress: ' + str(round(100 * progress / totalsize)) + '%') |
|
|
|
log.info('Extracting modpack and utilities...') |
|
|
|
with zipfile.ZipFile('modpack-proton.zip') as zipf: |
|
for zi in zipf.infolist(): |
|
zipf.extract(zi) |
|
date_time = time.mktime(zi.date_time + (0, 0, -1)) |
|
os.utime(zi.filename, (date_time, date_time)) |
|
|
|
os.remove('modpack-proton.zip') |
|
|
|
log.info('Modpack and utilities extracted!') |
|
|
|
except Exception as err: |
|
log.warn('Unable to download modpack and utilities. ' + str(err)) |
|
|
|
if os.path.exists('boss.exe'): |
|
log.info('Running BOSS...') |
|
wineexec(['boss.exe', '--update', '--silent', '--game', 'Fallout3']) |
|
log.info('BOSS list updated.') |
|
else: |
|
log.warn('BOSS not found.') |
|
|
|
if not os.path.islink('Fallout3.exe') and os.path.exists('Fallout3.fose.exe'): |
|
shutil.move('Fallout3.exe', 'Fallout3.orig.exe') |
|
|
|
# Not needed with PROTON_FORCE_LARGE_ADDRESS_AWARE=1 |
|
# if not os.path.exists('Fallout3.exe.Backup'): |
|
# log.info('Patching executable with 4GB support...') |
|
# wineexec(['4gb_patch.exe', 'Fallout3.orig.exe']) |
|
# log.info('Executable patched!') |
|
# else: |
|
# log.info('Executable already patched.') |
|
|
|
os.symlink('Fallout3.fose.exe', 'Fallout3.exe') |
|
log.info('Enabled FOSE. Fallout3.orig.exe is the real exe while Fallout3.exe is a link to a wrapper exe.') |
|
|
|
if os.path.exists('intel-fix.reg'): |
|
log.info('Applying registry keys...') |
|
wineexec(['regedit', '/C', 'intel-fix.reg']) |
|
|
|
if os.path.exists('d3d9.dll'): |
|
os.remove('d3d9.dll') |
|
if ENB: |
|
os.symlink('d3d9.enb.dll', 'd3d9.dll') |
|
util.set_ini_options(enb_opts, 'My Games/Fallout3/FalloutPrefs.ini', base_path='user') |
|
else: |
|
os.symlink('d3d9.wrapper.dll', 'd3d9.dll') |
|
|
|
if not LAUNCHER: |
|
util.replace_command('FalloutLauncher.exe', 'Fallout3.exe') |
|
log.info('Launching Fallout 3...') |
|
|
|
|
|
def wineexec(args=[], wait=True): |
|
env = dict(util.protonmain.g_session.env) |
|
env['WINEPREFIX'] = util.protonprefix() |
|
env['WINE'] = util.protonmain.g_proton.wine_bin |
|
env['WINELOADER'] = util.protonmain.g_proton.wine_bin |
|
env['WINESERVER'] = util.protonmain.g_proton.wineserver_bin |
|
env['LD_PRELOAD'] = '' |
|
|
|
subprocess.call([env['WINESERVER'], '-w'], env=env) |
|
|
|
if wait: |
|
process = subprocess.Popen([util.protonmain.g_proton.wine_bin] + args, env=env, stdout=sys.stdout, stderr=sys.stderr) |
|
process.wait() |
|
util._killhanging() |
|
else: |
|
process = subprocess.Popen([util.protonmain.g_proton.wine_bin] + args, env=env, close_fds=True) |
|
return True |
When I install protonfixes-git on Arch and run the 22370.py script stored in ~/.config/protonfixes/ I get the error:
Traceback (most recent call last): File "22370.py", line 4, in <module> from protonfixes import util File "/usr/lib/python3.8/site-packages/protonfixes/__init__.py", line 13, in <module> 'waitforexitandrun' in sys.argv[1], IndexError: list index out of range
If I install protonfixes and then run the script, I get no output in the terminal but it doesn't appear that the script did anything. There is no FOSE folder I can find and there are no .exe files other than the original Fallout3.exe file. I noticed when I started ipython and imported all of the modules in the script and then ran
os.makedirs(os.path.join(util.protonprefix(), 'drive_c/users/steamuser/My Documents/My Games/Fallout3'))
I get the error:Steam is installed to my root directory but the game is installed on another drive that is mounted to /media/MY_USERNAME/Linux. Do I need to set the environment variable or store the 22370 file elsewhere? I tried to copy it to different locations and run it nearer to the steam directory but I got the same results.
EDIT:
Should "protonfixes" be installed through pip3 as well? When I have protonfixes-git installed (protonfixes hasn't been updated since 2018) attempt to import all of the packages using lines 4-19 in ipython I get:
There is a steamapps/common/ directory in both my home folder and the directory where I have been installing games. a "Proton 4.11" directory is on the mounted non-OS drive and is where I edited the user settings file since I have had the most success running Proton 4.11 with Fallout 3.