Skip to content

Instantly share code, notes, and snippets.

@grepwood
Created September 20, 2018 01:07
Show Gist options
  • Save grepwood/ddc9e41779fa5414b9946fe4b212c84d to your computer and use it in GitHub Desktop.
Save grepwood/ddc9e41779fa5414b9946fe4b212c84d to your computer and use it in GitHub Desktop.
Setup Fallout 4 on Wine
#!/usr/bin/env bash
SCRIPT_NAME="$(basename "${0}")"
FALLOUT4_EXECUTABLE="Fallout4.exe"
WINE_REGISTRY_KEY="HKEY_CURRENT_USER\\Software\\Wine"
FALLOUT4_APPDEFAULTS_REGISTRY_KEY="HKEY_CURRENT_USER\\Software\\Wine\\AppDefaults\\${FALLOUT4_EXECUTABLE}"
FALLOUT4_INSTALL_REGISTRY_KEY='HKEY_LOCAL_MACHINE\Software\Bethesda Softworks\Fallout4'
USER_SHELL_FOLDERS_REGISTRY_KEY='HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Explorer\Shell Folders'
function die() {
printf "%s : %s() : %s\\n" "${SCRIPT_NAME}" "${FUNCNAME[1]}" "${1}" >&2
exit 1
}
# get_registry_value()
# Returns "data" from a specified Registry key and value, where "value"="data".
# Note: the Wine reg query result will use \r\n CR linefeeds - strip these!
function get_registry_value() {
local registry_key="${1}" registry_value="${2}"
declare -n data="${3}"
data="$(wine reg query "${registry_key}" /v "${registry_value}" 2>/dev/null \
| awk -F 'REG_[A-Z]*' \
-vregistry_value="${registry_value}" \
'{
for (i=1;i<=NF;++i)
gsub("(^[[:blank:]]*|[[:blank:]]*$)","",$i)
if ($1 == registry_value)
print $2
}' 2>/dev/null \
| sed 's/\r//g'
)"
[[ -z "${data}" ]] && die "Registry key data lookup [${registry_key}] \"${registry_value}\"=... failed"
}
# add_registry_value()
# Adds a Wine Registry key or a key plus "value"="data".
function add_registry_value() {
local registry_key="${1}" registry_value="${2}" data="${3}" type="${4}"
if [[ -z "${registry_value}" ]]; then
wine reg add "${registry_key}" /f 2>/dev/null \
|| die "Failed to add Registry key [${registry_key}]"
else
wine reg add "${registry_key}" /v "${registry_value}" /t "${type}" /d "${data}" /f 2>/dev/null \
|| die "Failed to add Registry key+value: [${registry_key}] \"${registry_value}\"=\"${data}\" (${type})"
fi
}
# delete_registry_value()
# Deletes a Wine Registry key or key plus value pair.
function delete_registry_value() {
local registry_key="${1}" registry_value="${2}"
if [[ -z "${registry_value}" ]]; then
wine reg delete "${registry_key}" /va /f &>/dev/null
else
wine reg delete "${registry_key}" /v "${registry_value}" /f &>/dev/null
fi
}
# convert_windows_to_unix_path()
# Convert a Windows path to a canonical Unix path - follows all symbolic links.
# Note: winepath outputs \r\n CR linefeeds - so we must strip these!
function convert_windows_to_unix_path() {
local windows_path="${1}" unix_path
declare -n canonical_unix_path="${2}"
unix_path="$(winepath -u ''"${windows_path}"'' 2>/dev/null | sed 's/\r//g')"
canonical_unix_path="$(readlink -f "${unix_path}")"
if [[ ! -d "${canonical_unix_path}" ]]; then
die "Unable to convert Windows directory: \"${windows_path}\"; to a Unix directory."
fi
}
# find_file()
# Finds a specified file, using a specified base directory+relative path offset.
# Note: must use a case-insensitive search!
function find_file() {
local search_path="${1}" relative_path="${2}" depth relative_path_regex
declare -n target="${3}"
[[ -d "${search_path}" ]] || die "File search directory does not exist \"${search_path}\""
depth="$(echo "${relative_path}" | awk -F'/' '{ print NF }')"
relative_path_regex=".*$(echo "${relative_path}" | sed 's/[-.()\/]/\\&/g' 2>/dev/null)"
# Without this last grep you will end up finding multiple directories
target="$(find "${search_path}" -mindepth "$((depth-1))" -maxdepth "$((depth))" -iregex "${relative_path_regex}" 2>/dev/null | grep "${2}")"
[[ -f "${target}" ]] || die "File search failed: \"${search_path}${relative_path}\""
}
# ini_file_overwrite_value()
# Add/Overwrite name=value pair in a specified .ini file section.
# Note: Windows .ini files will use \r\n CR linefeeds - don't break this!
function ini_file_overwrite_value() {
local ini_file="${1}" name="${2}" value="${3}" section="${4}"
[[ -f "${ini_file}" ]] || die ".ini file path not valid: \"${ini_file}\""
# shellcheck disable=SC1004
sed -i -e '\|^'"${name}"'=|d' -e '\|^\['"${section}"'\]|a\
'"${name}=${value}\\r" "${ini_file}" 2>/dev/null \
|| die "sed operation failed on: \"${ini_file}\""
}
# I don't like it when scripts are messy and dirty like they lack a main function.
# Makes it so much harder to read them.
function main {
[[ -z "${WINEPREFIX}" ]] && die "WINEPREFIX env variable not set"
[[ -d "${WINEPREFIX}" ]] || die "WINEPREFIX env variable is set to an invalid path: \"${WINEPREFIX}\""
((EUID==0)) && die "Do not run this script as the root user!"
# Get Wine user 'My Documents' folder from Wine Registry.
get_registry_value "${USER_SHELL_FOLDERS_REGISTRY_KEY}" \
'Personal' "WINDOWS_USER_DOCUMENTS_PATH"
convert_windows_to_unix_path "${WINDOWS_USER_DOCUMENTS_PATH}" "UNIX_USER_DOCUMENTS_PATH"
# Get install directory of Fallout 4 from Wine Registry.
get_registry_value "${FALLOUT4_INSTALL_REGISTRY_KEY}" \
'installed path' "WINDOWS_FALLOUT4_INSTALL_PATH"
convert_windows_to_unix_path "${WINDOWS_FALLOUT4_INSTALL_PATH}" "UNIX_FALLOUT4_INSTALL_PATH"
# Find 2 main Fallout 4 configuration files.
find_file "${UNIX_USER_DOCUMENTS_PATH}" '/My Games/Fallout4/Fallout4.ini' "USER_FALLOUT4_INI_FILE"
find_file "${UNIX_FALLOUT4_INSTALL_PATH}" 'Fallout4_Default.ini' "GAME_FALLOUT4DEFAULT_INI_FILE"
# Set bBackgroundMouse=1 : Switches between the game controlling the mouse position (0) or the operating system (1).
ini_file_overwrite_value "${USER_FALLOUT4_INI_FILE}" "bBackgroundMouse" "1" "Controls"
ini_file_overwrite_value "${GAME_FALLOUT4DEFAULT_INI_FILE}" "bBackgroundMouse" "1" "Controls"
# GrabFullscreen=Y
add_registry_value "${WINE_REGISTRY_KEY}\\X11 Driver" 'GrabFullscreen' 'Y' 'REG_SZ'
# Version=win7
add_registry_value "${FALLOUT4_APPDEFAULTS_REGISTRY_KEY}" 'Version' 'win7' 'REG_SZ'
# Setup winetricks xact
winetricks -q --force xact &>/dev/null || die "winetricks xact failed"
# Move xact audio Dll Overrides to Fallout 4 AppDefaults. So this set of DLL Overrides
# doesn't affect other games/applications in the same Wineprefix.
for dll in "xaudio2_0" "xaudio2_1" "xaudio2_2" "xaudio2_3" "xaudio2_4" "xaudio2_5" "xaudio2_6" "xaudio2_7" "x3daudio1_0" "x3daudio1_1" "x3daudio1_2" "x3daudio1_3" "x3daudio1_4" "x3daudio1_5" "x3daudio1_6" "x3daudio1_7"; do
delete_registry_value "${WINE_REGISTRY_KEY}\\DllOverrides" "*${dll}"
delete_registry_value "${WINE_REGISTRY_KEY}\\DllOverrides" "*${dll}"
delete_registry_value "${FALLOUT4_APPDEFAULTS_REGISTRY_KEY}\\DllOverrides" "*${dll}"
add_registry_value "${FALLOUT4_APPDEFAULTS_REGISTRY_KEY}\\DllOverrides" "${dll}" 'native,builtin' 'REG_SZ'
done
# Delete all xact DLL Overrides we don't required (2D audio as well?)
for dll in "xapofx1_1" "xapofx1_2" "xapofx1_3" "xapofx1_4" "xapofx1_5"; do
delete_registry_value "${WINE_REGISTRY_KEY}\\DllOverrides" "${dll}"
delete_registry_value "${WINE_REGISTRY_KEY}\\DllOverrides" "*${dll}"
done
}
main $@
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment