|
#!/bin/env bash
|
|
|
|
# Common launch options for all Steam client Windows/Linux games on Linux
|
|
# Copyright (C) 2018 Pekka Helenius
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation, either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
|
|
###########################################################
|
|
|
|
# Default target platform
|
|
# Valid platforms are: Windows, Linux
|
|
|
|
DEFAULT_PLATFORM="Windows"
|
|
SECOND_PLATFORM="Linux"
|
|
|
|
LAUNCH_OPTIONS_WINDOWS="WINEPATH=/usr/bin/ %command%"
|
|
LAUNCH_OPTIONS_LINUX="%command%"
|
|
|
|
###########################################################
|
|
|
|
# Default Steam client main folder path
|
|
|
|
STEAMPATH="$HOME/.local/share/Steam"
|
|
|
|
###########################################################
|
|
|
|
# http://wiki.bash-hackers.org/snipplets/print_horizontal_line#a_line_across_the_entire_width_of_the_terminal
|
|
function INFO_SEP() { printf '%*s\n' "${COLUMNS:-$(tput cols)}" '' | tr ' ' - ; }
|
|
|
|
###########################################################
|
|
|
|
if [[ ! -d ${STEAMPATH} ]]; then
|
|
INFO_SEP
|
|
echo -e "\n\e[1mError: Steam client folder not found for user $USER\e[0m\n\nAborting.\n"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ $(ps -o pid --no-headers -C steamwebhelper | wc -l) -ne 0 ]]; then
|
|
INFO_SEP
|
|
echo -e "\n\e[1mError: Steam client is running\e[0m\n\nYou must close your Steam client in order to run this script. Otherwise, launch options would not be accepted by the client.\n\nAborting.\n"
|
|
exit 1
|
|
fi
|
|
|
|
###########################################################
|
|
|
|
# Find file localconfig.vdf
|
|
LOCALCONF_FIND=$(find ${STEAMPATH}/userdata/*/config -type f -name "localconfig.vdf")
|
|
|
|
i=0
|
|
for conf in ${LOCALCONF_FIND[@]}; do
|
|
confs[$i]=${conf}
|
|
let i++
|
|
done
|
|
|
|
if [[ ${#confs[*]} -ne 1 ]]; then
|
|
echo -e "\n\e[1mWarning:\e[0m Multiple Steam account configuration files 'localconfig.vdf' found. Please select which one to update:\n"
|
|
|
|
i=0
|
|
for k in ${confs[*]}; do
|
|
conf_id=$(printf '%s' ${confs[$i]} | sed -r 's/^.*userdata\/(.*)\/config.*/\1/')
|
|
conf_user=$(grep -r "\"PersonaName\"" ${confs[$i]} | awk '{print $NF}' | sed 's/\"//g')
|
|
echo -e "$(( ${i} + 1 ))) ${conf_user} (ID: ${conf_id})"
|
|
let i++
|
|
done
|
|
|
|
echo ""
|
|
|
|
read -r -p "Selection: " -i "1" -e conf_select
|
|
|
|
if [[ ${conf_select} =~ ^[0-9]+$ ]]; then
|
|
conf_input=${confs[$(( ${conf_select} - 1 ))]}
|
|
else
|
|
echo -e "\e[1mError:\e[0m Number expected. Aborting.\n"
|
|
exit 1
|
|
fi
|
|
|
|
if [[ -n ${conf_input} ]]; then
|
|
LOCALCONF=${conf_input}
|
|
echo -e "Selecting configuration ${conf_select}\n"
|
|
else
|
|
echo -e "\e[1mError:\e[0m Valid configuration file could not be determined. Aborting.\n"
|
|
exit 1
|
|
fi
|
|
|
|
else
|
|
LOCALCONF=${confs}
|
|
fi
|
|
|
|
###########################################################
|
|
|
|
INFO_SEP
|
|
|
|
echo -e "\n\e[1mSteam Platform Target\e[0m\nPlease determine your target platform for Steam games launch option changes.\n\nValid options are\n\n1) ${DEFAULT_PLATFORM}\n2) ${SECOND_PLATFORM}\n"
|
|
|
|
read -r -p "" -i "1" -e PLATFORM
|
|
|
|
if [[ $(printf '%s' ${PLATFORM} | sed '/^\s*$/d') == "" ]]; then
|
|
echo -e "Warning: Platform not determined. Using default platform (${DEFAULT_PLATFORM}).\n"
|
|
PLATFORM=${DEFAULT_PLATFORM}
|
|
elif [[ ! $(printf '%s' ${PLATFORM} | sed '/^\s*$/d') =~ ^[1-2]$ ]]; then
|
|
echo -e "Not a valid value. Valid values are 1 and 2. Using default platform (${DEFAULT_PLATFORM}).\n"
|
|
PLATFORM=${DEFAULT_PLATFORM}
|
|
fi
|
|
|
|
if [[ $PLATFORM -eq 1 ]]; then
|
|
PLATFORM=${DEFAULT_PLATFORM}
|
|
elif [[ $PLATFORM -eq 2 ]]; then
|
|
PLATFORM=${SECOND_PLATFORM}
|
|
fi
|
|
|
|
if [[ $PLATFORM != "Windows" ]] && [[ $PLATFORM != "Linux" ]]; then
|
|
echo -e "Invalid platform '${PLATFORM}'. Aborting\n"
|
|
exit 1
|
|
fi
|
|
|
|
###########################################################
|
|
|
|
# Default platform specific launch options
|
|
|
|
if [[ $PLATFORM == "Windows" ]]; then
|
|
DEFAULT_LAUNCH_OPTIONS=${LAUNCH_OPTIONS_WINDOWS}
|
|
elif [[ $PLATFORM == "Linux" ]]; then
|
|
DEFAULT_LAUNCH_OPTIONS=${LAUNCH_OPTIONS_LINUX}
|
|
fi
|
|
|
|
###########################################################
|
|
|
|
echo -e "\nTarget platform: \e[1m${PLATFORM}\e[0m\n"
|
|
|
|
echo -e "\e[1mCommon launch options for all Steam client ${PLATFORM} games on Linux\e[0m\n"
|
|
|
|
INFO_SEP
|
|
|
|
echo -e "\e[1mWARNING:\e[0m This script overrides any launch options used for Steam client ${PLATFORM} games on Linux.\n\nDefault launch override options are as follows:\n\n\e[1m${DEFAULT_LAUNCH_OPTIONS}\e[0m\n\n\
|
|
If you want to use these options, press Enter. Otherwise, supply your own launch override \
|
|
string now.\n\e[1mNOTE:\e[0m Be aware that any previous overrides for ${PLATFORM} Steam games will be overwritten.\n"
|
|
|
|
read -r -p "" -i "${DEFAULT_LAUNCH_OPTIONS}" -e LAUNCH_OPTIONS_RAW
|
|
|
|
###########################################################
|
|
|
|
if [[ $(printf '%s' ${LAUNCH_OPTIONS_RAW} | sed '/^\s*$/d') == "" ]]; then
|
|
echo -e "Launch options are empty. Any previous launch options will be cleared.\n"
|
|
else
|
|
echo -e "\nLaunch options are:\n\e[1m${LAUNCH_OPTIONS_RAW}\e[0m\n"
|
|
fi
|
|
|
|
read -r -p "Confirm [Y/n] " -i "y" -e confirm
|
|
if [[ ! $(echo ${confirm} | sed '/^\s*$/d') =~ ^([yY][eE][sS]|[yY])$ ]]; then
|
|
echo -e "Aborting\n"
|
|
exit 0
|
|
fi
|
|
unset confirm
|
|
echo ""
|
|
|
|
###########################################################
|
|
|
|
# Commonly used Internal Field Separator for loops in this script
|
|
|
|
IFS=$'\n'
|
|
|
|
###########################################################
|
|
|
|
# Determine platform-specific games and their AppIDs
|
|
|
|
i=0
|
|
for game in $(find ${STEAMPATH}/steamapps/common/ -mindepth 1 -maxdepth 1 -type d -printf '%f\n'); do
|
|
gamedir="${STEAMPATH}/steamapps/common/${game}"
|
|
|
|
if [[ ${PLATFORM} == "Windows" ]]; then
|
|
bincmd=$(find "${gamedir}" -type f | sed -E '/.*\/.*\.exe$/!d')
|
|
elif [[ ${PLATFORM} == "Linux" ]]; then
|
|
bincmd=$(find "${gamedir}" -type f | sed -E '/.*\/.*\.[A-Za-z0-9]+$/d')
|
|
fi
|
|
|
|
for bin in ${bincmd}; do
|
|
if [[ $(file -bn ${bin}) == *${PLATFORM}* ]]; then
|
|
valid_games[$i]="${game}"
|
|
let i++
|
|
break
|
|
fi
|
|
done
|
|
done
|
|
|
|
if [[ -z ${valid_games[*]} ]]; then
|
|
echo -e "No ${PLATFORM} games found\n"
|
|
exit 0
|
|
fi
|
|
|
|
# Sort game list
|
|
valid_games=($(sort <<< "${valid_games[*]}"))
|
|
|
|
i=0
|
|
workdir=${PWD}
|
|
cd ${STEAMPATH}/steamapps/
|
|
for game_idfile in ${valid_games[@]}; do
|
|
games_appids[$i]=$(grep -l -d skip "${game_idfile}" * | grep -oE "[0-9]*")
|
|
let i++
|
|
done
|
|
cd ${workdir}
|
|
|
|
echo -e "Found local ${PLATFORM} games:\n\n$(i=0; for k in ${valid_games[*]}; do echo -e "$(( ${i} + 1 ))) ${k} (AppID: ${games_appids[$i]})"; let i++; done)\n\n"
|
|
|
|
###########################################################
|
|
|
|
echo -e "Select the game(s) you want to update [Default: all]\n"
|
|
|
|
read -r -p "" -i "all" -e SELECTED_GAMES
|
|
|
|
if [[ $(printf '%s' ${SELECTED_GAMES} | sed 's/[ \t]*$//') == "all" ]]; then
|
|
|
|
k=0
|
|
for h in ${valid_games[@]}; do
|
|
SELECTED_GAMES_NAMES[$k]=${h}
|
|
SELECTED_GAMES_APPIDS[$k]=${games_appids[$k]}
|
|
let k++
|
|
done
|
|
|
|
else
|
|
|
|
p=0
|
|
unset IFS
|
|
for game_selection in ${SELECTED_GAMES}; do
|
|
if [[ ${game_selection} =~ ^[0-9]+$ ]]; then
|
|
single_game=${games_appids[$(( ${game_selection} - 1 ))]}
|
|
if [[ -z ${single_game} ]]; then
|
|
echo -e "\e[1mWarning:\e[0m Game option ${game_selection} not in valid range\n"
|
|
else
|
|
gamelist[$p]="${single_game}"
|
|
let p++
|
|
fi
|
|
else
|
|
echo -e "\e[1mWarning:\e[0m Unrecognized game option ${game_selection}\n"
|
|
fi
|
|
done
|
|
IFS=$'\n'
|
|
|
|
# Sort game list, remove duplicates
|
|
gamelist=($(sort -u <<< "${gamelist[*]}"))
|
|
|
|
fi
|
|
|
|
if [[ -v gamelist ]]; then
|
|
x=0
|
|
for s in ${gamelist[*]}; do
|
|
y=0
|
|
for j in ${games_appids[*]}; do
|
|
if [[ "${s}" == "${j}" ]]; then
|
|
gamelist_names[$x]="${valid_games[$y]} (AppID: ${s})"
|
|
gamelist_appids[$x]="${s}"
|
|
let x++
|
|
fi
|
|
let y++
|
|
done
|
|
done
|
|
|
|
# Sort game list
|
|
SELECTED_GAMES_APPIDS=($(sort <<< "${gamelist_appids[*]}"))
|
|
SELECTED_GAMES_NAMES=($(sort <<< "${gamelist_names[*]}"))
|
|
|
|
echo -e "\nSelected games:\n$(for g in ${gamelist_names[*]}; do echo -e "- ${g}"; done)\n"
|
|
|
|
fi
|
|
|
|
###########################################################
|
|
|
|
read -r -p "Apply changes to the selected games [Y/n] " confirm
|
|
if [[ ! $(echo ${confirm} | sed '/^\s*$/d') =~ ^([yY][eE][sS]|[yY])$ ]]; then
|
|
echo -e "Aborting\n"
|
|
exit 0
|
|
fi
|
|
|
|
echo ""
|
|
|
|
###########################################################
|
|
|
|
d=0
|
|
for game_id in ${SELECTED_GAMES_APPIDS[@]}; do
|
|
|
|
echo -e "Applying launch options for ${SELECTED_GAMES_NAMES[$d]}"
|
|
|
|
# We seek these patterns in the ${LOCALCONF} file
|
|
pattern1="\"${game_id}\""
|
|
pattern2="\}$"
|
|
inclpattern="LastPlayed"
|
|
|
|
# These are raw line numbers which partially match our patterns
|
|
# We sort these in reverse order, so that the last found lines are listed first
|
|
# The first listed group of lines is the one we are interested in
|
|
lines_rawnumbers=$(sed -n "/${pattern1}/,/${pattern2}/=;/${pattern2}/{x;/${inclpattern}/=;s/.*//;x}" ${LOCALCONF} | sort -r)
|
|
|
|
# Pretty much same than above, but get only valid part from the ${LOCALCONF} file
|
|
# This match applies only, if all the following is valid:
|
|
# - starting pattern is ${pattern1}
|
|
# - ending pattern is ${pattern2}
|
|
# - there exists a third pattern ${inclpattern} between starting and ending patterns
|
|
#
|
|
lines_raw=$(sed -n "/${pattern1}/,/${pattern2}/{H};/${pattern2}/{x;/${inclpattern}/p;s/.*//;x}" ${LOCALCONF})
|
|
|
|
# If we can't determine a valid line range as specified in ${lines_raw}, shift the loop
|
|
# and continue to the next game
|
|
if [[ -z ${lines_raw} ]]; then
|
|
echo -e "Warning: Could not find valid entries for ${SELECTED_GAMES_NAMES[$d]}\n"
|
|
error=
|
|
shift
|
|
fi
|
|
|
|
# Just put lines_rawnumbers into a valid array 'linenumbers', nothing else
|
|
i=0
|
|
for line_rawnumber in ${lines_rawnumbers[*]}; do
|
|
linenumbers[$i]=${line_rawnumber}
|
|
let i++
|
|
done
|
|
|
|
# We don't need this variable anymore
|
|
unset lines_rawnumbers
|
|
|
|
# Determine/Calculate valid line number range for lines presented in ${lines_raw}
|
|
# We are interested only in the last uniform line range found in 'linenumbers' array
|
|
#
|
|
# We iterate through this uniform line range, starting from the largest number, which is
|
|
# the first index value in 'linenumbers' array
|
|
# We compare currently iterated line number to the next one in 'linenumbers' array
|
|
#
|
|
# The currently iterated line number is substracted by 1 and compared to
|
|
# the next 'linenumbers' array index value. If they match, we continue iteration.
|
|
# Once the first non-matching value pair is found, we determine the first line
|
|
# number, presented by variable 'first_rawline', adn break the loop
|
|
line_count=0
|
|
for line in ${linenumbers[@]}; do
|
|
if [[ $(( ${line} - 1 )) -ne ${linenumbers[$(( ${line_count} + 1 ))]} ]]; then
|
|
first_rawline=${linenumbers[${line_count}]}
|
|
break
|
|
fi
|
|
let line_count++
|
|
done
|
|
|
|
# We reversed the line numbers in 'linenumbers' array so the last line is actually the first value in this array
|
|
last_rawline=${linenumbers[0]}
|
|
|
|
# These are actual game specific option lines between brackets
|
|
# Numbers in these calculations have been determined by investigating
|
|
# the structure of ${LOCALCONF} file
|
|
first_optionline=$(( ${first_rawline} + 2 ))
|
|
last_optionline=$(( ${last_rawline} - 1 ))
|
|
|
|
# Determine how many prefix tabulators are needed for the "LaunchOptions" field in ${LOCALCONF} file
|
|
prefixtab_count=$(sed -n "${last_optionline}p" ${LOCALCONF} | cat -T -- | grep -o "^[\^I]*" | awk -F ^ '{print NF-1}')
|
|
|
|
# Print counted number of tabulator prefix characters for LaunchOptions field
|
|
prefixtabs=$(printf "\t%.0s" $(seq 1 $prefixtab_count))
|
|
|
|
# Final format of LaunchOptions field to be inserted into the ${LOCALCONF} file
|
|
LAUNCH_OPTIONS=$(printf '%s\"%s\"\t\t\"%s\"' ${prefixtabs} "LaunchOptions" ${LAUNCH_OPTIONS_RAW})
|
|
|
|
# Iterate through all game-specific lines
|
|
for dataline in $(seq ${first_optionline} ${last_optionline}); do
|
|
|
|
# If the current line has string "LaunchOptions", replace that line with the new one
|
|
# Break the loop after that, we don't need to iterate through other lines
|
|
if [[ $(sed -n "${dataline}p" ${LOCALCONF} | grep "\"LaunchOptions\"" | wc -l) -eq 1 ]]; then
|
|
OPTIONS=${LAUNCH_OPTIONS} LINE=${dataline} \
|
|
perl -npi -e 's/.*\n/$ENV{OPTIONS}\n/g if $.==$ENV{LINE}' ${LOCALCONF}
|
|
break
|
|
fi
|
|
|
|
# If the current line is the last option line for the game and "LaunchOptions" field has not been encountered yet, append our "LaunchOptions" field as the last field for this game
|
|
# Break the loop after that
|
|
if [[ ${dataline} -eq ${last_optionline} ]]; then
|
|
prevline_contents=$(sed -n "${dataline}p" ${LOCALCONF})
|
|
|
|
OPTIONS=${LAUNCH_OPTIONS} LINE=${dataline} PREVCONTENTS=${prevline_contents} \
|
|
perl -npi -e 's/.*\n/$ENV{PREVCONTENTS}\n$ENV{OPTIONS}\n/g if $.==$ENV{LINE}' ${LOCALCONF}
|
|
break
|
|
|
|
fi
|
|
|
|
done
|
|
|
|
let d++
|
|
done
|
|
|
|
if [[ -v error ]]; then
|
|
echo -e "\nSomething went wrong. Check messages above.\n"
|
|
exit 1
|
|
else
|
|
echo -e "\nDone. You can start your Steam client.\n"
|
|
exit 0
|
|
fi
|