Easily set launch options for your Windows/Linux Steam games (single/multiple/all) via CLI on Linux
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 

400 lines
13 KiB

#!/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