Skip to content

Instantly share code, notes, and snippets.

@daniel-j
Last active September 9, 2020 04:34
Show Gist options
  • Save daniel-j/e89004d6d5548d648701883e736ce213 to your computer and use it in GitHub Desktop.
Save daniel-j/e89004d6d5548d648701883e736ce213 to your computer and use it in GitHub Desktop.
Fallout 3 GOTY script for Protonfixes

Fallout 3 Game of the Year Edition script for Protonfixes

This is ONLY for the Game of the Year Edition! Regular Fallout 3 edition is not supported/tested.

How to install

If you already have a working setup of Fallout 3 GOTY, I do not recommend using this script as it may break your current mods etc.

Before using this script, MAKE A BACKUP of your saves in steamapps/compatdata/22370/pfx/drive_c/users/steamuser/My Documents/My Games/Fallout3/Saves. Then delete the directory steamapps/compatdata/22370 and/or reinstall the game in Steam.

Install LATEST Protonfixes (anything newer than December 2018 for Proton 4.11-12). If you're on Arch Linux: yay -S protonfixes-git. This installs a Python module that we will load into Proton. Find your steamapps/common/Proton XXX directory, where XXX is the version number. Make a copy of user_settings.sample.py and rename it to user_settings.py. Open it in a text editor and add import protonfixes at the end. Comment the line with WINEDEBUG if you want, it impacts performance to have debugging enabled.

Alternatively, you can install a build of ProtonGE which includes protonfixes.

Save the script seen below as ~/.config/protonfixes/localfixes/22370.py.

Open a terminal and run (Arch Linux tested) tail -f /tmp/dumps/${USER}_stdout.txt. This shows output from Steam Client in realtime. Now, run Fallout 3 GOTY in Steam. You should see some blue output in the terminal, this is from the Protonfixes script.

When run first time, it downloads a modpack that includes basic mods, a custom ENBSeries config, scripts and more. See README djazz's fallout 3 goty modpack proton.txt for details on what's included.

If you want to run the game in borderless mode, open Data/FOSE/Plugins/OneTweak.ini and set BorderlessWindow=true. This does not work with D9VK/DXVK.

The following launch argument environment variables have been added:

  • ENB=1 enables ENB (installs some extra winetricks, disables d3d9-wrapper, as they use same filename)
  • LAUNCHER=0 Disables launcher, launch the game directly.

Example: ENB=1 LAUNCHER=0 %command%

""" 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
This modpack and collection of fixes has been compiled by djazz.
It contains the following:
> Fallout Script Extender (FOSE) (v1.3 beta 2)
This is a scripting framework for many popular mods/plugins.
> F3TF - Fallout 3 Tick Fix (FOSE Plugin) (v1.3)
This fixes microstuttering, and allows the game to be exited faster.
Read more: https://www.nexusmods.com/fallout3/mods/23542
> OneTweak for FO3 (FOSE Plugin) (v2.0.0.1)
Borderless Windowed Mode for Fallout 3. This makes it possible to Alt+Tab out of the game.
Enable this by editing OneTweak.ini and set BorderlessWindow to true.
This disables regular fullscreen and windowed mode.
Does not work with D9VK/DXVK, hence why it's disabled by default here.
Read more: https://www.nexusmods.com/fallout3/mods/21337
> NVAC - New Vegas Anti Crash (FOSE Plugin) (v7.5.1.0)
Implements structured exception handling and sanity checking to reduce frequency of game crashes.
> Updated Unofficial Fallout 3 Patch (v2.3.2)
This mod fixes errors and bugs in the FO3 + DLC esms.
Cut content is reviewed/restored only if fixable and able to blend into the game perfectly.
Read more: https://www.nexusmods.com/fallout3/mods/19122
> Compiled fixes for Fallout 3 (v1.0)
Fixes more things in the game.
Read more: https://www.nexusmods.com/fallout3/mods/23584
> Tinker Joe fix for UOF3P (v1.0)
Fixes the issue where you can't recruit RL-3.
Use this only if running the unofficial patch.
Read more: https://www.nexusmods.com/fallout3/mods/23584
> Safe - Safe Regulator HQ (v1.0)
Disables two animal spawn points at the Regulator HQ.
Early in the game they are usually only dogs but late in the game they may spawn Death Claws.
It's odd seeing these creatures using the HQ as their hangout out spot when turning in fingers.
Read more: https://www.nexusmods.com/fallout3/mods/11853
> djazz - Jury St. Station train fix (v1.1)
Fixes a rendering glitch that causes some train carts to go invisible in a certain angle.
Compatible with UF3P. Modifies JuryStStation02's roommarkers and adds a portal.
Here is a video of the glitch: https://www.youtube.com/watch?v=kYQkVc4j0lw
> d3d9-wrapper (v1.14)
Limits fps to 60.
Source: https://github.com/ThirteenAG/d3d9-wrapper
> Updated OGG/Vorbis libraries
It's said that this improves performance.
> Force GPU to NVIDIA 980 Ti (intel-fix.reg)
This is needed to make the game run on Intel graphics.
When using with ENB, this also fixes the bad water rendering on AMD gpus.
> NTCore 4GB Patch (v1.0.0.1)
Patches the Fallout 3 exe to support more than 2 GB of memory.
If using Steam Play/Proton, set PROTON_FORCE_LARGE_ADDRESS_AWARE=1 instead.
Read more: https://ntcore.com/?page_id=371
> Custom ENB (uses v0.322 of ENBSeries)
Based on Atmos ENB and Midhrastic ENB.
Enable by setting "ENB=1 %command%" in Steam Launch Arguments.
Modify enbseries.ini to tweak settings.
You can toggle it on and off with Shift+F12 in-game.
Open the in-game config menu with Shift+Enter.
> Fallout3.fose.exe
This is a shortcut that launches the game with FOSE loaded
Fallout3.exe should be a symbolic link to Fallout3.fose.exe
See Fallout3.fose.py for source code.
Compiled with PyInstaller.
> BOSS (v2.3.2)
Setting load order. Only executed when modpack has been downloaded.
Read more: https://boss-developers.github.io
@mjd119
Copy link

mjd119 commented Sep 9, 2020

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:

Traceback (most recent call last)
<ipython-input-3-7e4aba4984af> in <module>
----> 1 os.makedirs(os.path.join(util.protonprefix(), 'drive_c/users/steamuser/My Documents/My Games/Fallout3'))

/usr/lib/python3.8/site-packages/protonfixes/util.py in protonprefix()
     46 
     47     return os.path.join(
---> 48         os.environ['STEAM_COMPAT_DATA_PATH'],
     49         'pfx/')
     50 

/usr/lib/python3.8/os.py in __getitem__(self, key)
    673         except KeyError:
    674             # raise KeyError with the original key value
--> 675             raise KeyError(key) from None
    676         return self.decodevalue(value)
    677 

KeyError: 'STEAM_COMPAT_DATA_PATH'

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:

IndexError                                Traceback (most recent call last)
<ipython-input-3-78df49ce5f79> in <module>
----> 1 from protonfixes import util
      2 from protonfixes.logger import log

/usr/lib/python3.8/site-packages/protonfixes/__init__.py in <module>
     11     'STEAM_COMPAT_DATA_PATH' in os.environ,
     12     'PROTONFIXES_DISABLE' not in os.environ,
---> 13     'waitforexitandrun' in sys.argv[1],
     14 ]
     15 

IndexError: list index out of range

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment